前向声明模板函数的必要性

spr*_*aff 19 c++ variadic-functions one-definition-rule variadic-templates c++11

我最近创建了这个示例代码来说明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"
Run Code Online (Sandbox Code Playgroud)

如果foo省略了前向声明的前两行,则会打印出来int:123int:123.这让一位经验丰富,知识渊博的C++程序员感到惊讶.

他确信前向声明不应该是必要的,因为在两阶段查找的第二阶段之前,身体不会被实例化.他认为编译器(gcc 4.6)有一个bug.

我相信编译器是正确的,因为这两个foo不同的基本模板函数,并且在第一阶段需要锁定基本模板的选择,否则你可能通过foo在定义所有版本之前实例化来违反单定义规则然后再考虑(考虑链接器假设冗余模板函数定义是相同的,可互换的和可丢弃的).

那么,谁是对的?


上面链接的GOTW很好地解释了函数模板不是部分特殊化的原因和原因,但是可变参数模板函数的存在似乎增加了混淆 - foo<int,Tail...>应该是部分特化的foo<Head,Tail...>直觉比非直觉更强可变函数,至少对我而言.

Mat*_* M. 10

GCC(和Clang)是对的.MSVC会错误,因为它没有正确实现查找.

似乎是你的同事误解了.查找规则是:

  • 在从定义调用Base模板函数之前,需要声明它
  • 在实例化之前,需要声明Specialized模板函数

注意:这些规则适用于自由函数,在类中不需要前向声明

请注意,因为定义也充当声明,所以在您的示例中,转发声明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
Run Code Online (Sandbox Code Playgroud)

如果有可用的基本模板,则这是一个错误.如果在实例化之前未声明特化,则不会使用它,并且这可能随后意味着违反ODR规则(如果另一个实例化使用特化).

从标准(C++ 0x FDIS):

14.6.4.2

1.对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1,3.4.2,3.4.3)找到候选函数,除了:

- 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,仅找到模板定义上下文中的函数声明.

- 对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.

如果函数名称是非限定id并且调用将是格式错误或者找到更好的匹配,则相关命名空间内的查找会考虑所有在所有翻译单元中的那些名称空间中引入外部链接的函数声明,而不仅仅是考虑在模板定义和模板实例化上下文中找到的那些声明,则程序具有未定义的行为.

请注意,所提到的段落适用于常规功能.


yme*_*ett 7

两阶段查找将找到:

  • 在定义点可见的函数,和
  • ADL在实例化时可以找到的函数.

template <typename Head, typename... Tail> void foo (Head x, Tail... tail) ADL无法找到,因此如果在定义时不可见,则根本无法找到.

换句话说,海湾合作委员会是对的.