使用聚合初始化模拟默认函数参数是否存在任何陷阱?
Are there any pitfalls to simulate defaulted function parameters using aggregate initialization?
问题:
例如,如果你想要一个可变参数函数,它接受任意数量的参数Args&&...args
,并t
次打印所有这些参数。更重要的是,您希望t
默认为1
,因此默认情况下它会一次性打印所有args
。
您要尝试的第一件事是:
template <typename ... Args>
void foo(Args... args, unsigned t = 1) {
for (unsigned i = 0; i < t; ++i) {
(std::cout << ... << args);
}
}
显然,除非您显式传入模板参数,否则这不起作用:
// Error: expected 1 argument, got 2
foo(0, "Hello, world!");
因为默认参数在模板推导时被视为普通参数,并且参数包始终为空。这会阻止您使函数有用。(相关问题(
然后,我决定使用聚合初始化(特别是自 c++ 20 以来指定的初始值设定项(来模拟更强大的"默认参数"。它看起来像这样:
struct foo_t {
unsigned t = 1;
template <typename ... Args>
void operator() (Args... args) {
for (unsigned i = 0; i < t; ++i) {
(std::cout << ... << args);
}
}
};
int main() {
foo_t{}("Hello, ", "World!n"); // prints 1 line
foo_t{ 5 }(0, "Hello, world!n"); // prints 5 lines
return 0;
}
此外,这可以解决人们在 c++ 20 指定初始化器的帮助下无法"跳过"默认函数参数的抱怨:
struct bar_t {
const std::string& str = "Hello, world!";
int t = 1;
void operator() () {
for (int i = 0; i < t; ++i) {
std::cout << str << std::endl;
}
}
};
int main() {
// Skips .str, using the default "Hello, World!"
bar_t{ .t = 10 }();
return 0;
}
我想知道这样做是否有任何潜在的陷阱。
背景(可以安全地忽略(
所以昨天我在SO周围徘徊,遇到了一个问题(但后来被删除了(,询问如何将默认std::source_location
参数与可变参数模板相结合:
template<typename... Args>
void log(Args&&... args, const std::experimental::source_location& location = std::experimental::source_location::current()) {
std::cout << location.line() << std::endl;
}
显然,正如问题中所述,这并没有按预期工作。所以我想出了以下代码:
struct logger {
const std::experimental::source_location& location = std::experimental::source_location::current();
template <typename... Args>
void operator() (Args&&... args) {
std::cout << location.line() << std::endl;
}
};
int main(int argc, char** argv) {
logger{}("I passed", argc, "arguments.");
return 0;
}
但发现它可以做得更多,因此这个问题。
寿命(扩展(至少有一个陷阱:
const std::string& str = "Hello, world!";
创建悬空指针(成员没有生存期延长(。
以下很好:
void repeat_print(const std::string& str = "Hello, world!", int t = 1) {/*..*/}
int main()
{
repeat_print();
}
但以下不是:
struct bar_t {
const std::string& str = "Hello, world!";
int t = 1;
void operator() () const { /*..*/ }
};
int main()
{
bar_t{}();
}
您可以修复bar_t
以按值获取成员,但在某些情况下,您会执行额外的复制。
相关文章:
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 是否可以对零模板参数进行模板专门化
- 函数作为模板参数,是否对返回类型强制约束
- visual是否可以在c++中创建一个接收无限数量相同类型(或至少相当数量)参数的函数
- 函数是否可以访问传递给main()的参数
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- 如何检查给定的参数是否为 cv::noArray()?
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 如果返回 -1,时间() 的参数是否被修改?
- 用于检查值是否为其任何参数的帮助程序函数
- 将类型声明为类型模板参数的模板参数的一部分是否合法?
- 模板化检查是否存在带有参数列表的类成员函数?
- 我如何知道作为参数的size_t在函数中是否有效?
- 是否可以在C++中有一个"generic"模板参数,该参数可以是非类型模板参数或类型?
- 是否可以就地构造一个固定大小的数组作为函数参数?
- C++方法是否可以根据传递给构造函数的参数具有不同的返回类型?
- 是否可以在运行时强制转换模板参数?
- 是否可以在不填充自己的参数的情况下将模板函数作为参数传递?
- 编译器是否强制根据模板参数计算表达式?
- C++中大多数/所有 setter 函数的参数是否应该写为常量引用?