我最近创建了这个示例代码来说明C ++ 11可变参数模板函数的用法。
template <typename Head, typename... Tail> void foo (Head, Tail...);
template <typename... Tail> void foo (int, Tail...);
void foo () {}
template <typename... Tail>
void foo (int x, Tail... tail)
{
std :: cout << "int:" << x;
foo (tail...);
}
template <typename Head, typename... Tail>
void foo (Head x, Tail... tail)
{
std :: cout << " ?:" << x;
foo (tail...);
}
foo (int (123), float (123)); // Prints "int:123 ?:123.0"
如果省略了前向声明foo
的前两行,则会打印int:123int:123
。这让一位经验丰富且知识渊博的C ++程序员感到惊讶。
他确信前向声明不应该是必要的,因为在两阶段查找的第二阶段之前,身体不会被实例化。他认为编译器(gcc 4.6)有一个错误。
我认为编译器是正确的,因为两个foo
是different base template functions,并且在第一阶段需要锁定基本模板的选择,否则你可能会违反一个定义规则在定义所有版本之前实例化foo
然后再次实例化(考虑链接器假设冗余模板函数定义是相同的,可互换的和可丢弃的)。
那么,谁是对的?
上面链接的GOTW很好地解释了函数模板不是部分特殊化的原因和原因,但可变参数模板函数的存在似乎增加了混淆 - foo<int,Tail...>
应该是部分特化的直觉对于非变量函数,foo<Head,Tail...>
比直觉强,至少对我而言。
答案 0 :(得分:10)
似乎是你的同事误解了。查找规则是:
注意:这些规则适用于自由函数,在类中不需要前向声明
请注意,因为定义也充当声明,所以在您的示例中,不必转发声明int
版本。
正确的例子:
template <typename T> void foo(T); // declare foo<T>
template <typename T> void bar(T t) { foo(t); }// call foo<T> (dependent context)
template <> void foo<int>(int); // declare specialiaztion foo<int>
void bar(int i) { foo(i); } // instantiate foo<T> with int
// which is the specialization
如果有可用的基本模板,则这是一个错误。如果在实例化之前未声明特化,则不会使用它,并且这可能随后意味着违反ODR规则(如果另一个实例化使用特化)。
来自标准(C ++ 0x FDIS):
<强> 14.6.4.2 强>
1。对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1,3.4.2,3.4.3)找到候选函数,除了:< / p>
- 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,只能找到模板定义上下文中的函数声明。
- 对于使用关联命名空间(3.4.2)的查找部分,只能找到模板定义上下文或模板实例化上下文中找到的函数声明。
如果函数名称是unqualified-id并且调用结果不正确或者找到更好的匹配,则相关名称空间中的查找会考虑所有在所有翻译单元中的名称空间中引入外部链接的函数声明,不仅仅考虑在模板定义和模板实例化上下文中找到的那些声明,那么程序还有未定义的行为。
请注意,所引用的段落适用于常规功能。
答案 1 :(得分:7)
两阶段查找将找到:
template <typename Head, typename... Tail> void foo (Head x, Tail... tail)
,因此如果在定义时不可见,则根本无法找到它。
换句话说,海湾合作委员会是对的。