Python 可变参数详解与代码示例
文章目录
- [Python 可变参数详解与代码示例](#Python 可变参数详解与代码示例)
-
- 什么是可变参数?
- 一、基本语法和使用
-
- [1. `*args` - 位置参数收集器](#1.
*args- 位置参数收集器) - [2. `**kwargs` - 关键字参数收集器](#2.
**kwargs- 关键字参数收集器)
- [1. `*args` - 位置参数收集器](#1.
- [二、组合使用 `*args` 和 `**kwargs`](#二、组合使用
*args和**kwargs) - 三、实际应用场景
-
- [1. 通用日志记录函数](#1. 通用日志记录函数)
- [2. 数据库查询构建器](#2. 数据库查询构建器)
- [3. 配置项合并函数](#3. 配置项合并函数)
- 四、高级用法
-
- [1. 参数解包(Unpacking)](#1. 参数解包(Unpacking))
- [2. 强制关键字参数](#2. 强制关键字参数)
- [3. 装饰器中的可变参数](#3. 装饰器中的可变参数)
- [4. 类方法中的可变参数](#4. 类方法中的可变参数)
- 五、注意事项和最佳实践
- 六、常见错误和陷阱
- 总结
- 现代C++中的可变参数详解与代码示例
-
- 一、C风格可变参数函数
- 二、初始化列表(C++11)
- 三、可变参数模板(C++11)
-
- [1. 基本可变参数模板](#1. 基本可变参数模板)
- [2. 可变参数模板实际应用示例](#2. 可变参数模板实际应用示例)
- 四、折叠表达式(C++17)
- 五、参数包展开技巧
- 六、完美转发和可变参数
- 七、实际应用示例
-
- [1. 通用配置管理器](#1. 通用配置管理器)
- [2. 元编程工具](#2. 元编程工具)
- [3. 测试框架模拟](#3. 测试框架模拟)
- 八、C++20新特性:概念(Concepts)与可变参数
- 九、性能考虑和最佳实践
- 十、完整示例:通用工厂模式
- 总结对比表
- 最佳实践建议
什么是可变参数?
可变参数允许函数接受不定数量的参数,是Python函数定义中非常有用的特性。Python提供了两种可变参数:
*args- 接受任意数量的位置参数**kwargs- 接受任意数量的关键字参数
一、基本语法和使用
1. *args - 位置参数收集器
python
def print_args(*args):
"""接收任意数量的位置参数"""
print(f"args的类型: {type(args)}")
print(f"args的值: {args}")
print(f"参数个数: {len(args)}")
for i, arg in enumerate(args, 1):
print(f"第{i}个参数: {arg}")
print("-" * 30)
# 测试不同数量的参数
print_args() # 无参数
print_args(1) # 一个参数
print_args(1, 2, 3) # 多个参数
print_args("a", "b", "c", "d") # 多个字符串参数
# 混合类型参数
print_args(1, "hello", [1, 2, 3], {"name": "Alice"}, 3.14)
2. **kwargs - 关键字参数收集器
python
def print_kwargs(**kwargs):
"""接收任意数量的关键字参数"""
print(f"kwargs的类型: {type(kwargs)}")
print(f"kwargs的值: {kwargs}")
print(f"关键字参数个数: {len(kwargs)}")
for key, value in kwargs.items():
print(f"{key} = {value}")
print("-" * 30)
# 测试不同关键字参数
print_kwargs() # 无参数
print_kwargs(name="Alice", age=25)
print_kwargs(city="Beijing", country="China", population=21700000)
print_kwargs(x=10, y=20, color="red", shape="circle", radius=5)
# 注意:关键字参数必须是合法的标识符(不能以数字开头)
print_kwargs(**{"name": "Bob", "age": 30}) # 使用字典解包
二、组合使用 *args 和 **kwargs
python
def mixed_args(*args, **kwargs):
"""同时接收位置参数和关键字参数"""
print("位置参数 (args):")
if args:
for i, arg in enumerate(args, 1):
print(f" {i}: {arg}")
else:
print(" 无")
print("\n关键字参数 (kwargs):")
if kwargs:
for key, value in kwargs.items():
print(f" {key}: {value}")
else:
print(" 无")
print("=" * 40)
# 测试
mixed_args()
mixed_args(1, 2, 3)
mixed_args(a=1, b=2, c=3)
mixed_args(1, 2, 3, name="Alice", age=25)
mixed_args("hello", "world", x=10, y=20, z=30)
# 注意:顺序很重要!必须是 *args 在前,**kwargs 在后
def correct_order(a, b, *args, **kwargs):
"""正确的参数顺序:普通参数 -> *args -> **kwargs"""
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"kwargs={kwargs}")
print("\n正确的参数顺序示例:")
correct_order(1, 2, 3, 4, 5, x=10, y=20)
# 错误的顺序会导致语法错误
# def wrong_order(**kwargs, *args): # SyntaxError
# pass
三、实际应用场景
1. 通用日志记录函数
python
def log_message(level, *args, **kwargs):
"""
通用日志记录函数
Args:
level: 日志级别 (info, warning, error)
*args: 日志消息部分
**kwargs: 额外信息(时间戳、模块名等)
"""
import datetime
# 默认的额外信息
default_kwargs = {
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"module": "__main__",
}
# 更新用户提供的额外信息
default_kwargs.update(kwargs)
# 构建消息
message = " ".join(str(arg) for arg in args)
# 输出格式化日志
log_entry = f"[{default_kwargs['timestamp']}] [{level.upper()}]"
log_entry += f" [{default_kwargs['module']}] {message}"
print(log_entry)
# 返回完整的日志数据
return {
"level": level,
"message": message,
**default_kwargs
}
# 使用示例
print("日志记录示例:")
log_message("info", "程序启动")
log_message("warning", "磁盘空间不足", module="disk_utils")
log_message("error", "无法连接到数据库",
module="database",
retry_count=3,
timeout=30)
log_message("info", "用户登录成功",
module="auth",
username="alice",
ip="192.168.1.100")
2. 数据库查询构建器
python
def build_sql_query(table, *conditions, **params):
"""
构建SQL查询语句
Args:
table: 表名
*conditions: 查询条件
**params: 查询参数 (limit, order_by, select_fields等)
"""
# 默认参数
default_params = {
"select_fields": "*",
"limit": None,
"order_by": None,
"distinct": False,
}
# 更新用户提供的参数
default_params.update(params)
# 构建SELECT子句
if default_params["distinct"]:
select_clause = f"SELECT DISTINCT {default_params['select_fields']}"
else:
select_clause = f"SELECT {default_params['select_fields']}"
# 构建FROM子句
from_clause = f"FROM {table}"
# 构建WHERE子句
if conditions:
where_clause = "WHERE " + " AND ".join(conditions)
else:
where_clause = ""
# 构建ORDER BY子句
if default_params["order_by"]:
order_clause = f"ORDER BY {default_params['order_by']}"
else:
order_clause = ""
# 构建LIMIT子句
if default_params["limit"]:
limit_clause = f"LIMIT {default_params['limit']}"
else:
limit_clause = ""
# 组合所有子句
query_parts = [select_clause, from_clause, where_clause, order_clause, limit_clause]
query = " ".join(part for part in query_parts if part)
return query
# 使用示例
print("\nSQL查询构建器示例:")
# 简单查询
query1 = build_sql_query("users")
print(f"查询1: {query1}")
# 带条件的查询
query2 = build_sql_query("users", "age > 18", "status = 'active'")
print(f"查询2: {query2}")
# 带多个参数的查询
query3 = build_sql_query(
"products",
"category = 'electronics'",
"price < 1000",
select_fields="id, name, price",
order_by="price DESC",
limit=10
)
print(f"查询3: {query3}")
# DISTINCT查询
query4 = build_sql_query(
"orders",
distinct=True,
select_fields="customer_id"
)
print(f"查询4: {query4}")
3. 配置项合并函数
python
def merge_configs(*configs, **overrides):
"""
合并多个配置字典,后面的配置会覆盖前面的
Args:
*configs: 多个配置字典
**overrides: 需要覆盖的配置项
"""
result = {}
# 合并所有配置字典
for config in configs:
if config is not None:
result.update(config)
# 应用覆盖项
result.update(overrides)
return result
# 使用示例
print("\n配置合并示例:")
# 基础配置
base_config = {
"host": "localhost",
"port": 8080,
"debug": False,
"timeout": 30,
}
# 开发环境配置
dev_config = {
"debug": True,
"log_level": "DEBUG",
}
# 测试环境配置
test_config = {
"host": "test.example.com",
"log_level": "INFO",
}
# 合并配置
merged_config = merge_configs(base_config, dev_config, test_config)
print(f"合并后的配置: {merged_config}")
# 合并并覆盖特定项
custom_config = merge_configs(
base_config,
dev_config,
timeout=60, # 覆盖timeout
max_connections=100 # 添加新配置
)
print(f"自定义配置: {custom_config}")
# 链式合并
chain_config = merge_configs(
{"a": 1, "b": 2},
{"b": 3, "c": 4},
{"c": 5, "d": 6},
e=7 # 关键字参数添加
)
print(f"链式合并: {chain_config}")
四、高级用法
1. 参数解包(Unpacking)
python
def function(a, b, c):
"""普通的三参数函数"""
return f"a={a}, b={b}, c={c}"
# 使用 * 解包序列
print("\n参数解包示例:")
args_list = [1, 2, 3]
print(f"使用列表解包: {function(*args_list)}")
args_tuple = (4, 5, 6)
print(f"使用元组解包: {function(*args_tuple)}")
# 使用 ** 解包字典
kwargs_dict = {"a": 7, "b": 8, "c": 9}
print(f"使用字典解包: {function(**kwargs_dict)}")
# 混合解包
def advanced_function(a, b, c, d, e):
"""多参数函数"""
return f"a={a}, b={b}, c={c}, d={d}, e={e}"
partial_args = [1, 2]
partial_kwargs = {"d": 4, "e": 5}
print(f"混合解包: {advanced_function(*partial_args, 3, **partial_kwargs)}")
2. 强制关键字参数
python
def create_user(name, *, email, phone=None, age=None):
"""
使用 * 强制后面的参数必须使用关键字参数
Args:
name: 用户名(位置参数)
*: 强制后面参数为关键字参数
email: 邮箱(必须关键字参数)
phone: 电话(可选关键字参数)
age: 年龄(可选关键字参数)
"""
user = {"name": name, "email": email}
if phone is not None:
user["phone"] = phone
if age is not None:
user["age"] = age
return user
print("\n强制关键字参数示例:")
# 正确使用
user1 = create_user("Alice", email="alice@example.com")
print(f"user1: {user1}")
user2 = create_user("Bob", email="bob@example.com", phone="123456789", age=25)
print(f"user2: {user2}")
# 错误使用 - 会报错
# user3 = create_user("Charlie", "charlie@example.com") # TypeError
# user4 = create_user("David", "david@example.com", 30) # TypeError
3. 装饰器中的可变参数
python
import time
import functools
def timer(func):
"""计算函数执行时间的装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"函数 {func.__name__} 执行时间: {end_time - start_time:.6f}秒")
return result
return wrapper
def retry(max_attempts=3, delay=1):
"""重试装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第{attempt}次尝试失败: {e}")
if attempt < max_attempts:
print(f"{delay}秒后重试...")
time.sleep(delay)
raise Exception(f"函数 {func.__name__} 在{max_attempts}次尝试后仍失败")
return wrapper
return decorator
@timer
def calculate_sum(*numbers):
"""计算任意数量数字的和"""
return sum(numbers)
@retry(max_attempts=3, delay=0.5)
def unreliable_function(x, y):
"""模拟不可靠的函数"""
import random
if random.random() < 0.7: # 70%的失败率
raise ValueError("随机失败!")
return x + y
print("\n装饰器中的可变参数示例:")
# 测试计时装饰器
print(f"求和结果: {calculate_sum(1, 2, 3, 4, 5)}")
print(f"求和结果: {calculate_sum(10, 20, 30)}")
# 测试重试装饰器
try:
result = unreliable_function(10, 20)
print(f"不可靠函数结果: {result}")
except Exception as e:
print(f"最终失败: {e}")
4. 类方法中的可变参数
python
class Calculator:
"""计算器类,演示类方法中的可变参数"""
def __init__(self, name="默认计算器"):
self.name = name
self.history = []
def add(self, *numbers):
"""计算任意数量数字的和"""
result = sum(numbers)
self.history.append(f"add{numbers} = {result}")
return result
def multiply(self, *numbers, round_digits=None):
"""计算任意数量数字的乘积"""
import math
result = math.prod(numbers)
if round_digits is not None:
result = round(result, round_digits)
self.history.append(f"multiply{numbers} = {result}")
return result
def calculate_average(self, *numbers, **options):
"""计算平均值,支持多种选项"""
if not numbers:
return 0
# 处理选项
ignore_zeros = options.get("ignore_zeros", False)
ignore_negatives = options.get("ignore_negatives", False)
decimal_places = options.get("decimal_places", 2)
# 过滤数字
filtered_numbers = []
for num in numbers:
if ignore_zeros and num == 0:
continue
if ignore_negatives and num < 0:
continue
filtered_numbers.append(num)
if not filtered_numbers:
return 0
# 计算平均值
average = sum(filtered_numbers) / len(filtered_numbers)
average = round(average, decimal_places)
self.history.append(f"average{numbers} = {average}")
return average
def show_history(self):
"""显示计算历史"""
print(f"\n{self.name} 的计算历史:")
for i, record in enumerate(self.history, 1):
print(f"{i}. {record}")
print("\n类方法中的可变参数示例:")
calc = Calculator("我的计算器")
print(f"加法: {calc.add(1, 2, 3, 4, 5)}")
print(f"乘法: {calc.multiply(2, 3, 4)}")
print(f"带精度的乘法: {calc.multiply(1.5, 2.5, 3.5, round_digits=2)}")
print(f"平均值: {calc.calculate_average(1, 2, 3, 4, 5)}")
print(f"忽略零的平均值: {calc.calculate_average(0, 1, 2, 0, 3, ignore_zeros=True)}")
print(f"忽略负数的平均值: {calc.calculate_average(-1, 2, -3, 4, ignore_negatives=True)}")
print(f"带精度的平均值: {calc.calculate_average(1, 2, 3, 4, decimal_places=4)}")
calc.show_history()
五、注意事项和最佳实践
python
def demonstrate_best_practices():
"""
演示可变参数的最佳实践和注意事项
"""
# 1. 避免在 *args 和 **kwargs 中进行复杂的处理
def good_example(*args, **kwargs):
"""好的示例:明确处理参数"""
# 确保参数是期望的类型
numbers = [arg for arg in args if isinstance(arg, (int, float))]
settings = {k: v for k, v in kwargs.items() if not k.startswith('_')}
return numbers, settings
# 2. 为可变参数提供默认值
def with_defaults(*args, default_value=0, **kwargs):
"""为可变参数提供默认值"""
if not args:
args = (default_value,)
# 设置kwargs的默认值
kwargs.setdefault('verbose', False)
kwargs.setdefault('max_retries', 3)
return args, kwargs
# 3. 使用类型提示(Python 3.5+)
from typing import Any, Dict, List, Union
def typed_function(*args: Union[int, float], **kwargs: Any) -> Dict[str, Any]:
"""使用类型提示的可变参数函数"""
return {
"args_count": len(args),
"args_sum": sum(args),
"kwargs_keys": list(kwargs.keys())
}
# 4. 参数验证
def validated_function(*args, min_args=1, max_args=10, **kwargs):
"""对可变参数进行验证"""
# 验证位置参数数量
if len(args) < min_args:
raise ValueError(f"至少需要{min_args}个位置参数")
if len(args) > max_args:
raise ValueError(f"最多接受{max_args}个位置参数")
# 验证关键字参数
required_keys = {"name", "email"}
missing_keys = required_keys - set(kwargs.keys())
if missing_keys:
raise ValueError(f"缺少必需的关键字参数: {missing_keys}")
return args, kwargs
# 5. 文档字符串
def well_documented_function(*args, **kwargs):
"""
为可变参数函数编写良好的文档字符串
Args:
*args: 位置参数,用于传递数值
**kwargs: 关键字参数,支持的选项包括:
- verbose (bool): 是否显示详细信息
- timeout (int): 超时时间(秒)
- retry (int): 重试次数
Returns:
tuple: 包含处理后的args和kwargs
Raises:
ValueError: 当参数不符合要求时
"""
# 函数实现
processed_args = [arg * 2 for arg in args]
processed_kwargs = {k.upper(): v for k, v in kwargs.items()}
return processed_args, processed_kwargs
print("最佳实践示例:")
print(f"好示例: {good_example(1, 2, 3, debug=True, _internal='no')}")
print(f"默认值: {with_defaults(default_value=100)}")
print(f"类型提示: {typed_function(1, 2, 3, name='test')}")
try:
validated_function(name="Alice", email="alice@example.com")
except ValueError as e:
print(f"参数验证: {e}")
print(f"文档化函数: {well_documented_function(1, 2, 3, verbose=True, timeout=30)}")
demonstrate_best_practices()
六、常见错误和陷阱
python
print("\n常见错误和陷阱:")
# 1. 可变参数的顺序错误
def correct_function(a, b, *args, **kwargs):
"""正确的顺序"""
pass
# def wrong_function1(**kwargs, *args): # 语法错误
# pass
# def wrong_function2(*args, a, b): # 语法错误,*args后不能有位置参数
# pass
# 2. 重复的参数名
def problematic_function(a, b=1, *args, **kwargs):
"""
这里没有问题,但调用时要注意:
不能通过args传递a或b,也不能通过kwargs传递a或b
"""
return a, b, args, kwargs
# 这会引发错误
# result = problematic_function(1, 2, 3, a=4) # TypeError: 重复的参数'a'
# 3. 可变参数的默认值
# def wrong_default(*args=[]): # 语法错误,不能为*args设置默认值
# pass
# def wrong_default_kwargs(**kwargs={}): # 语法错误,不能为**kwargs设置默认值
# pass
# 4. 在解包时的错误
def simple_func(a, b, c):
return a + b + c
data = [1, 2]
# result = simple_func(*data) # TypeError: 缺少参数'c'
# 正确的做法
result = simple_func(*data, 3)
print(f"正确的解包: {result}")
# 5. 意外的关键字参数
def strict_function(a, b, *, c):
"""强制c为关键字参数"""
return a + b + c
# strict_function(1, 2, 3) # TypeError: 需要关键字参数'c'
# 6. 可变参数和固定参数的混合使用
def mixed_usage(first, *middle, last):
"""
这种用法合法但不推荐,因为容易混淆
调用时:mixed_usage(1, 2, 3, 4, last=5)
"""
return first, middle, last
print(f"混合使用: {mixed_usage(1, 2, 3, 4, last=5)}")
总结
主要用途:
- 提高函数灵活性:让函数能够处理不同数量的参数
- 函数包装和装饰器:传递参数给被包装的函数
- API设计:创建灵活、可扩展的接口
- 配置管理:处理多个配置来源和覆盖项
选择指南:
- 使用
*args当:需要收集不确定数量的位置参数 - 使用
**kwargs当:需要收集不确定数量的关键字参数 - 使用
*当:需要强制后面的参数使用关键字形式传递
最佳实践:
- 为可变参数函数编写清晰的文档字符串
- 对参数进行适当的验证
- 使用类型提示提高代码可读性
- 避免在可变参数中进行过于复杂的处理
- 注意参数的顺序:普通参数 ->
*args->**kwargs
可变参数是Python强大灵活性的体现,正确使用可以使代码更加简洁、通用和可维护。
现代C++中的可变参数详解与代码示例
现代C++提供了多种可变参数机制,每种都有不同的用途和特点。以下是主要的方法:
一、C风格可变参数函数
这是从C继承的机制,使用<cstdarg>头文件,但类型不安全,不推荐在现代C++中使用。
cpp
#include <iostream>
#include <cstdarg>
// C风格可变参数 - 不推荐在现代C++中使用
int c_style_sum(int count, ...) {
int sum = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i) {
sum += va_arg(args, int);
}
va_end(args);
return sum;
}
void c_style_print(const char* format, ...) {
va_list args;
va_start(args, format);
char buffer[256];
vsnprintf(buffer, sizeof(buffer), format, args);
std::cout << buffer;
va_end(args);
}
二、初始化列表(C++11)
std::initializer_list用于传递同类型值的列表,主要用于构造函数和函数参数。
cpp
#include <iostream>
#include <vector>
#include <initializer_list>
#include <algorithm>
#include <numeric>
class MathOperations {
public:
// 1. 初始化列表构造函数
MathOperations(std::initializer_list<int> values)
: data(values.begin(), values.end()) {
std::cout << "构造函数: 接收了 " << values.size() << " 个元素\n";
}
// 2. 使用初始化列表作为参数
static int sum(std::initializer_list<int> numbers) {
return std::accumulate(numbers.begin(), numbers.end(), 0);
}
static double average(std::initializer_list<double> numbers) {
if (numbers.size() == 0) return 0.0;
double total = std::accumulate(numbers.begin(), numbers.end(), 0.0);
return total / numbers.size();
}
static void print_all(std::initializer_list<std::string> items) {
for (const auto& item : items) {
std::cout << "- " << item << "\n";
}
}
private:
std::vector<int> data;
};
// 初始化列表在标准库中的应用
void std_lib_initializer_list() {
std::cout << "\n=== 标准库中的初始化列表 ===\n";
// 容器初始化
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
// 动态数组
auto arr = {10, 20, 30, 40}; // 类型为 std::initializer_list<int>
// 在算法中使用
int max_val = std::max({10, 30, 20, 50, 40});
std::cout << "最大值: " << max_val << "\n";
int min_val = std::min({10, 30, 20, 50, 40});
std::cout << "最小值: " << min_val << "\n";
}
三、可变参数模板(C++11)
这是现代C++中最强大和类型安全的可变参数机制。
1. 基本可变参数模板
cpp
#include <iostream>
#include <string>
#include <type_traits>
// 递归终止函数(基例)
void print() {
std::cout << "\n"; // 所有参数处理完毕,换行
}
// 可变参数模板函数
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first;
if constexpr (sizeof...(args) > 0) {
std::cout << ", ";
print(args...); // 递归调用
} else {
std::cout << "\n";
}
}
// 使用折叠表达式(C++17)的简化版本
template<typename... Args>
void print_fold(Args&&... args) {
// C++17 折叠表达式
((std::cout << std::forward<Args>(args) << " "), ...);
std::cout << "\n";
}
// 计算参数个数
template<typename... Args>
constexpr std::size_t count_args(Args&&...) {
return sizeof...(Args);
}
// 通用求和函数
template<typename T>
T sum(T value) {
return value;
}
template<typename T, typename... Args>
T sum(T first, Args... args) {
return first + sum(args...);
}
// 类型安全的格式化输出
template<typename... Args>
void format_print(const std::string& fmt, Args&&... args) {
// 模拟简单格式化,实际应用中可以使用fmtlib等库
std::string result = fmt;
// 这里只是演示,实际格式化需要更复杂的实现
std::cout << "格式化字符串: " << fmt << "\n";
std::cout << "参数个数: " << sizeof...(args) << "\n";
}
2. 可变参数模板实际应用示例
cpp
#include <iostream>
#include <memory>
#include <tuple>
#include <utility>
// 1. 工厂函数
template<typename T, typename... Args>
std::unique_ptr<T> make_unique_factory(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
class Widget {
public:
Widget(int id, std::string name, double value)
: id(id), name(std::move(name)), value(value) {
std::cout << "创建Widget: " << id << ", " << name << ", " << value << "\n";
}
private:
int id;
std::string name;
double value;
};
// 2. 日志记录器
class Logger {
public:
template<typename... Args>
static void log(const std::string& level, Args&&... args) {
std::cout << "[" << level << "] ";
((std::cout << std::forward<Args>(args) << " "), ...);
std::cout << "\n";
}
template<typename... Args>
static void debug(Args&&... args) {
log("DEBUG", std::forward<Args>(args)...);
}
template<typename... Args>
static void info(Args&&... args) {
log("INFO", std::forward<Args>(args)...);
}
template<typename... Args>
static void error(Args&&... args) {
log("ERROR", std::forward<Args>(args)...);
}
};
// 3. 元组操作
template<typename... Args>
auto make_reverse_tuple(Args&&... args) {
return std::make_tuple(args...);
}
// 4. 参数转发
template<typename Func, typename... Args>
auto call_with_log(Func&& func, Args&&... args) {
std::cout << "调用函数,参数个数: " << sizeof...(args) << "\n";
auto start = std::chrono::high_resolution_clock::now();
// 完美转发参数
auto result = std::forward<Func>(func)(std::forward<Args>(args)...);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double>(end - start);
std::cout << "函数执行时间: " << duration.count() << "秒\n";
return result;
}
四、折叠表达式(C++17)
折叠表达式是C++17对可变参数模板的重大改进,大大简化了代码。
cpp
#include <iostream>
#include <vector>
#include <functional>
template<typename... Args>
void fold_expressions_demo(Args... args) {
std::cout << "\n=== 折叠表达式演示 ===\n";
// 1. 一元右折叠 (args + ...)
auto sum = (args + ...);
std::cout << "求和: " << sum << "\n";
// 2. 一元左折叠 (... + args)
auto sum_left = (... + args);
std::cout << "左折叠求和: " << sum_left << "\n";
// 3. 带初始值的二元右折叠 (init + ... + args)
auto sum_with_init = (0 + ... + args);
std::cout << "带初始值的求和: " << sum_with_init << "\n";
// 4. 逗号运算符折叠
((std::cout << args << " "), ...);
std::cout << "\n";
// 5. 逻辑运算
bool all_true = (true && ... && (args > 0));
std::cout << "所有参数都大于0: " << std::boolalpha << all_true << "\n";
// 6. 调用函数
(std::invoke([](auto arg) {
std::cout << "处理: " << arg << "\n";
}, args), ...);
}
// 实用折叠表达式函数
template<typename... Args>
bool all_of(Args... args) {
return (args && ...); // 所有参数都为真
}
template<typename... Args>
bool any_of(Args... args) {
return (args || ...); // 任意参数为真
}
template<typename... Args>
auto max_of(Args... args) {
// 需要至少一个参数,可以使用编译时检查
static_assert(sizeof...(args) > 0, "至少需要一个参数");
return std::max({args...});
}
// 打印任意类型和数量的参数
template<typename... Args>
void smart_print(Args&&... args) {
// 使用lambda处理每个参数
auto printer = [](const auto& arg) {
if constexpr (std::is_arithmetic_v<std::decay_t<decltype(arg)>>) {
std::cout << "[数值: " << arg << "] ";
} else if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::string>) {
std::cout << "[字符串: \"" << arg << "\"] ";
} else if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, const char*>) {
std::cout << "[C字符串: \"" << arg << "\"] ";
} else {
std::cout << "[对象: " << typeid(arg).name() << "] ";
}
};
(printer(args), ...); // 折叠表达式调用printer
std::cout << "\n";
}
五、参数包展开技巧
cpp
#include <iostream>
#include <tuple>
#include <type_traits>
#include <vector>
#include <map>
// 1. 参数包展开的多种方式
template<typename... Args>
class ParameterPackDemo {
public:
// 静态断言检查所有类型
static_assert((std::is_arithmetic_v<Args> && ...),
"所有类型必须是算术类型");
// 使用sizeof...获取参数数量
static constexpr size_t size = sizeof...(Args);
// 存储为元组
using TupleType = std::tuple<Args...>;
// 打印所有类型
static void print_types() {
std::cout << "参数包包含 " << size << " 个类型: ";
((std::cout << typeid(Args).name() << " "), ...);
std::cout << "\n";
}
};
// 2. 创建类型列表
template<typename... Ts>
struct TypeList {};
// 3. 遍历参数包的索引技巧
template<typename... Args, typename Func>
void for_each_arg(Func&& func, Args&&... args) {
// C++17折叠表达式版本
(std::forward<Func>(func)(std::forward<Args>(args)), ...);
}
template<typename... Args, typename Func>
void for_each_arg_with_index(Func&& func, Args&&... args) {
size_t index = 0;
(std::forward<Func>(func)(std::forward<Args>(args), index++), ...);
}
// 4. 构建容器
template<typename... Args>
std::vector<std::common_type_t<Args...>> make_vector(Args&&... args) {
std::vector<std::common_type_t<Args...>> vec;
vec.reserve(sizeof...(args));
(vec.push_back(std::forward<Args>(args)), ...);
return vec;
}
// 5. 编译时字符串连接
template<typename... Args>
constexpr auto concatenate_strings(Args&&... args) {
// 计算总长度
constexpr size_t total_len = ((std::string_view(args).size()) + ...);
// 创建结果数组
std::array<char, total_len + 1> result{};
// 复制每个字符串
char* ptr = result.data();
((ptr = std::copy_n(args, std::string_view(args).size(), ptr)), ...);
*ptr = '\0';
return result;
}
六、完美转发和可变参数
cpp
#include <iostream>
#include <memory>
#include <utility>
// 1. 完美转发可变参数
template<typename Func, typename... Args>
decltype(auto) perfect_forward(Func&& func, Args&&... args) {
return std::forward<Func>(func)(std::forward<Args>(args)...);
}
// 2. 创建任意类型对象
template<typename T, typename... Args>
T create_object(Args&&... args) {
return T(std::forward<Args>(args)...);
}
// 3. 带条件的参数转发
template<typename Func, typename... Args>
auto forward_if(Func&& func, bool condition, Args&&... args) {
if (condition) {
return std::forward<Func>(func)(std::forward<Args>(args)...);
} else {
// 返回默认构造的值
using ReturnType = decltype(func(std::forward<Args>(args)...));
if constexpr (!std::is_void_v<ReturnType>) {
return ReturnType{};
}
}
}
// 4. 可变参数lambda
void variadic_lambda_demo() {
std::cout << "\n=== 可变参数Lambda演示 ===\n";
// C++14起支持泛型lambda
auto variadic_lambda = [](auto&&... args) {
std::cout << "接收了 " << sizeof...(args) << " 个参数\n";
((std::cout << "参数: " << args << "\n"), ...);
};
variadic_lambda(1, 2.5, "hello", 'A');
// 带完美转发的lambda
auto forwarding_lambda = [](auto&& func, auto&&... args) {
return std::forward<decltype(func)>(func)(
std::forward<decltype(args)>(args)...
);
};
auto add = [](int a, int b) { return a + b; };
std::cout << "转发lambda结果: " << forwarding_lambda(add, 10, 20) << "\n";
}
七、实际应用示例
1. 通用配置管理器
cpp
#include <iostream>
#include <string>
#include <unordered_map>
#include <variant>
#include <any>
#include <typeindex>
class ConfigManager {
public:
// 存储任意类型的配置值
using ConfigValue = std::variant<
int,
double,
bool,
std::string,
std::vector<int>,
std::vector<std::string>
>;
private:
std::unordered_map<std::string, ConfigValue> configs;
public:
// 设置单个配置
template<typename T>
void set(const std::string& key, T&& value) {
configs[key] = std::forward<T>(value);
}
// 批量设置配置
template<typename... Pairs>
void set_many(Pairs&&... pairs) {
(set(std::get<0>(pairs), std::get<1>(pairs)), ...);
}
// 获取配置值
template<typename T>
T get(const std::string& key, T default_value = T{}) const {
auto it = configs.find(key);
if (it == configs.end()) {
return default_value;
}
try {
return std::get<T>(it->second);
} catch (const std::bad_variant_access&) {
return default_value;
}
}
// 打印所有配置
void print_all() const {
std::cout << "\n=== 配置信息 ===\n";
for (const auto& [key, value] : configs) {
std::cout << key << ": ";
std::visit([](const auto& v) {
using T = std::decay_t<decltype(v)>;
if constexpr (std::is_same_v<T, std::vector<int>>) {
std::cout << "[";
for (size_t i = 0; i < v.size(); ++i) {
std::cout << v[i];
if (i != v.size() - 1) std::cout << ", ";
}
std::cout << "]";
} else if constexpr (std::is_same_v<T, std::vector<std::string>>) {
std::cout << "[";
for (size_t i = 0; i < v.size(); ++i) {
std::cout << "\"" << v[i] << "\"";
if (i != v.size() - 1) std::cout << ", ";
}
std::cout << "]";
} else {
std::cout << v;
}
}, value);
std::cout << "\n";
}
}
};
2. 元编程工具
cpp
#include <iostream>
#include <type_traits>
// 1. 类型特性检查
template<typename... Ts>
struct AllSameType {
static constexpr bool value = (std::is_same_v<Ts, std::tuple_element_t<0, std::tuple<Ts...>>> && ...);
};
// 2. 编译时计算参数数量
template<typename... Args>
constexpr size_t count_args_at_compile_time = sizeof...(Args);
// 3. 检查参数包中是否包含特定类型
template<typename T, typename... Args>
struct ContainsType {
static constexpr bool value = (std::is_same_v<T, Args> || ...);
};
// 4. 获取参数包中第N个类型
template<size_t N, typename... Ts>
struct NthType {
using type = std::tuple_element_t<N, std::tuple<Ts...>>;
};
// 5. 将参数包转换为类型列表
template<typename... Ts>
struct TypeList2 {
template<template<typename...> class T>
using apply = T<Ts...>;
};
// 实用宏(编译时调试)
#define STATIC_PRINT(value) \
static_assert((value, true), #value " = " + std::to_string(value))
// 示例使用
void metaprogramming_demo() {
std::cout << std::boolalpha;
std::cout << "\n=== 元编程演示 ===\n";
std::cout << "所有类型相同<int, int, int>: "
<< AllSameType<int, int, int>::value << "\n";
std::cout << "所有类型相同<int, double, int>: "
<< AllSameType<int, double, int>::value << "\n";
std::cout << "包含类型<int, double, char>中是否有double: "
<< ContainsType<double, int, double, char>::value << "\n";
std::cout << "参数数量<int, double, char>: "
<< count_args_at_compile_time<int, double, char> << "\n";
using ThirdType = NthType<2, int, double, char, float>::type;
std::cout << "第三个类型是: " << typeid(ThirdType).name() << "\n";
}
3. 测试框架模拟
cpp
#include <iostream>
#include <vector>
#include <functional>
#include <string>
class TestFramework {
struct TestCase {
std::string name;
std::function<void()> func;
};
std::vector<TestCase> test_cases;
size_t passed = 0;
size_t failed = 0;
public:
// 注册测试用例
template<typename Func, typename... Args>
void register_test(const std::string& name, Func&& func, Args&&... args) {
test_cases.push_back({
name,
[=]() {
std::cout << "运行测试: " << name << "\n";
try {
std::invoke(func, args...);
std::cout << "✓ 测试通过\n";
++passed;
} catch (const std::exception& e) {
std::cout << "✗ 测试失败: " << e.what() << "\n";
++failed;
} catch (...) {
std::cout << "✗ 测试失败: 未知异常\n";
++failed;
}
std::cout << "---\n";
}
});
}
// 运行所有测试
void run_all() {
std::cout << "\n=== 运行测试 ===\n";
passed = 0;
failed = 0;
for (const auto& test : test_cases) {
test.func();
}
std::cout << "\n=== 测试结果 ===\n";
std::cout << "总计: " << (passed + failed) << "\n";
std::cout << "通过: " << passed << "\n";
std::cout << "失败: " << failed << "\n";
}
};
// 测试函数示例
void test_addition() {
if (1 + 1 != 2) {
throw std::runtime_error("1+1应该等于2");
}
}
void test_multiplication(int a, int b, int expected) {
if (a * b != expected) {
throw std::runtime_error("乘法测试失败");
}
}
八、C++20新特性:概念(Concepts)与可变参数
cpp
#if __cplusplus >= 202002L
#include <concepts>
#include <iostream>
// 1. 使用概念约束可变参数
template<std::integral... Args>
auto sum_integers(Args... args) {
return (args + ...);
}
template<std::floating_point... Args>
auto sum_floats(Args... args) {
return (args + ...);
}
// 2. 混合类型的概念约束
template<typename... Args>
requires (std::constructible_from<std::string, Args> && ...)
auto make_strings(Args&&... args) {
return std::make_tuple(std::string(std::forward<Args>(args))...);
}
// 3. 可变参数概念
template<typename T, typename... Args>
concept AllConvertibleTo = (std::convertible_to<Args, T> && ...);
template<typename T, AllConvertibleTo<T>... Args>
std::vector<T> make_vector(Args&&... args) {
std::vector<T> result;
result.reserve(sizeof...(args));
(result.push_back(std::forward<Args>(args)), ...);
return result;
}
void cpp20_concepts_demo() {
std::cout << "\n=== C++20 概念与可变参数 ===\n";
// 只能用于整数类型
auto int_sum = sum_integers(1, 2, 3, 4, 5);
std::cout << "整数和: " << int_sum << "\n";
// 只能用于浮点类型
auto float_sum = sum_floats(1.1, 2.2, 3.3);
std::cout << "浮点数和: " << float_sum << "\n";
// 创建字符串向量
auto strings = make_vector<std::string>("hello", "world", "!");
std::cout << "字符串向量: ";
for (const auto& s : strings) {
std::cout << s << " ";
}
std::cout << "\n";
}
#endif
九、性能考虑和最佳实践
cpp
#include <iostream>
#include <chrono>
#include <functional>
class PerformanceDemo {
public:
// 1. 编译时与运行时性能
template<typename... Args>
static constexpr auto compile_time_sum(Args... args) {
return (args + ...); // 在编译时计算
}
// 2. 避免递归深度过大
template<typename... Args>
static auto iterative_sum(Args... args) {
// 使用折叠表达式代替递归,避免深度问题
return (args + ...);
}
// 3. 参数包大小的影响
template<typename... Args>
static void benchmark_pack_size() {
constexpr size_t size = sizeof...(Args);
auto start = std::chrono::high_resolution_clock::now();
// 模拟处理参数包
auto result = (0 + ... + Args{}());
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double, std::milli>(end - start);
std::cout << "处理 " << size << " 个参数的耗时: "
<< duration.count() << " ms\n";
}
// 4. 完美转发与移动语义
template<typename... Args>
static auto create_with_forward(Args&&... args) {
// 完美转发避免不必要的拷贝
return std::make_tuple(std::forward<Args>(args)...);
}
};
// 最佳实践示例
template<typename... Args>
void best_practices_demo(Args&&... args) {
// 1. 使用折叠表达式代替递归
static_assert(sizeof...(args) < 1000, "参数过多可能影响编译时间");
// 2. 使用完美转发
auto tuple = std::make_tuple(std::forward<Args>(args)...);
// 3. 编译时检查类型
if constexpr ((std::is_arithmetic_v<std::decay_t<Args>> && ...)) {
std::cout << "所有参数都是算术类型\n";
}
// 4. 避免过大的参数包
constexpr size_t arg_count = sizeof...(args);
if constexpr (arg_count > 100) {
std::cout << "警告:参数包较大 (" << arg_count << " 个参数)\n";
}
}
十、完整示例:通用工厂模式
cpp
#include <iostream>
#include <memory>
#include <map>
#include <functional>
#include <any>
// 通用对象工厂
class ObjectFactory {
private:
using Creator = std::function<std::any()>;
std::map<std::string, Creator> creators;
public:
// 注册创建器
template<typename T, typename... Args>
void register_type(const std::string& name, Args&&... default_args) {
creators[name] = [default_args...]() -> std::any {
return std::make_any<T>(default_args...);
};
}
// 可变参数创建
template<typename T, typename... Args>
std::unique_ptr<T> create_with_args(const std::string& name, Args&&... args) {
auto it = creators.find(name);
if (it == creators.end()) {
throw std::runtime_error("类型未注册: " + name);
}
try {
// 尝试使用存储的创建器
if constexpr (sizeof...(args) == 0) {
auto obj = std::any_cast<T>(it->second());
return std::make_unique<T>(std::move(obj));
} else {
// 使用提供的参数创建
return std::make_unique<T>(std::forward<Args>(args)...);
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("类型转换失败: " + name);
}
}
// 批量注册
template<typename... Pairs>
void register_multiple(Pairs&&... pairs) {
(register_type_from_pair(std::forward<Pairs>(pairs)), ...);
}
private:
template<typename T, typename... Args>
void register_type_from_pair(const std::pair<std::string, std::tuple<Args...>>& pair) {
const auto& [name, args_tuple] = pair;
std::apply([this, &name](auto&&... args) {
register_type<T>(name, std::forward<decltype(args)>(args)...);
}, args_tuple);
}
};
// 使用示例
class Animal {
public:
virtual ~Animal() = default;
virtual void speak() const = 0;
};
class Dog : public Animal {
std::string name;
int age;
public:
Dog(std::string name = "Dog", int age = 1) : name(std::move(name)), age(age) {}
void speak() const override {
std::cout << name << " (年龄: " << age << ") 说: 汪汪!\n";
}
};
class Cat : public Animal {
std::string name;
public:
Cat(std::string name = "Cat") : name(std::move(name)) {}
void speak() const override {
std::cout << name << " 说: 喵喵!\n";
}
};
void factory_demo() {
std::cout << "\n=== 通用工厂模式示例 ===\n";
ObjectFactory factory;
// 注册类型(带默认参数)
factory.register_type<Dog>("Dog", "Buddy", 3);
factory.register_type<Cat>("Cat", "Whiskers");
// 使用默认参数创建
auto dog1 = factory.create_with_args<Dog>("Dog");
dog1->speak();
// 使用自定义参数创建
auto dog2 = factory.create_with_args<Dog>("Dog", "Max", 5);
dog2->speak();
auto cat = factory.create_with_args<Cat>("Cat", "Mittens");
cat->speak();
}
总结对比表
| 特性 | C风格可变参数 | std::initializer_list |
可变参数模板 | 折叠表达式 |
|---|---|---|---|---|
| 引入版本 | C++98/C | C++11 | C++11 | C++17 |
| 类型安全 | ✗ 不安全 | ✓ 安全(同类型) | ✓ 安全(任意类型) | ✓ 安全 |
| 编译时计算 | ✗ 运行时 | ✗ 运行时 | ✓ 编译时 | ✓ 编译时 |
| 性能 | 一般 | 良好 | 优秀 | 优秀 |
| 使用场景 | 兼容C代码 | 初始化列表 | 通用模板编程 | 简化参数包处理 |
| 推荐程度 | 不推荐 | 特定场景使用 | 推荐 | 强烈推荐 |
最佳实践建议
- 优先使用可变参数模板:类型安全且灵活
- 使用折叠表达式:简化代码,提高可读性
- 结合完美转发:避免不必要的拷贝
- 使用概念约束(C++20):提高代码安全性
- 注意编译时开销:过大的参数包可能影响编译时间
- 考虑递归深度:使用折叠表达式代替递归
- 提供清晰的错误信息:使用静态断言帮助调试
现代C++的可变参数机制非常强大,正确使用可以编写出既灵活又类型安全的代码,是泛型编程和元编程的重要基础。