【发布时间】:2020-04-28 07:40:16
【问题描述】:
因为8.3.6 ([dcl.fct.default])/4,
在给定的函数声明中,带有默认实参的形参后面的每个形参都应在此声明或先前声明中提供默认实参,或者应为函数形参包。
以下应该编译:
#include <iostream>
template<typename ...Ts>
void foo(int i=8, Ts... args)
{
std::cout << "Default parameters \t= " << i << "\n";
std::cout << "Additional params \t= " << sizeof...(Ts) << "\n";
}
int main()
{
foo(); // calls foo<>(int)
foo(1, "str1", "str2"); // calls foo<const char*, const char*>(int, const char*, const char*)
foo("str3"); // ERROR: does not call foo<const char*>(int, const char*)
return 0;
}
但由于foo("str3") 混淆了编译器,它无法编译。它抱怨没有匹配的函数来调用foo(const char*),并且它无法将"str3"(类型const char*)转换为类型int。
我知道可以通过函数重载或使用命名参数习语(参见where to place default value parameter in variable-length function in c++? 和default arguments and variadic functions)来解决这个问题。但是,我想知道编译器是否只是愚蠢的,或者是否有真正的原因导致上面代码示例中的预期行为没有实现。换句话说,为什么即使我将函数显式实例化为foo<const char*>(int, const char*),编译器也会抱怨?就好像显式实例化只是忽略了默认参数的值。为什么?
【问题讨论】:
-
您引用的段落仅适用于声明,不适用于函数调用。所以函数声明确实应该编译(它确实),但最后一次调用没有(并且没有理由应该)。此外,您没有在任何地方执行显式实例化。最后,添加参数包并不意味着您可以忽略可选参数的位置要求...
-
@MaxLanghof 是的,上面的代码没有明确的实例化。但是我对上述显式实例化进行了同样的尝试。它不编译。鉴于如果我将默认参数放在参数包的右侧,它也不会编译,在我看来,可变参数函数中默认参数的概念根本不存在。是这样吗?
-
当然存在。您的前两个函数调用表明它工作得很好。您实际上可以将两个可选的东西(可选参数和可能为空的参数包)放在同一个函数中,但这并不能让您摆脱关于参数位置的约束。如果您的函数参数以可选参数开头,则您要么根本不提供参数,要么必须为可选参数提供值。后续参数是可变参数还是可选参数对此没有任何影响。
标签: c++ variadic-templates default-value function-templates explicit-instantiation