简体   繁体   English

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

[英]Necessity of forward-declaring template functions

I recently created this example code to illustrate C++11 variadic template function usage. 我最近创建了这个示例代码来说明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"

If the first two lines which forward-declare foo are omitted then this prints int:123int:123 instead. 如果省略了前向声明foo的前两行,则打印int:123int:123 This surprised a certain experienced and knowledgeable C++ programmer. 这让一位经验丰富,知识渊博的C ++程序员感到惊讶。

He was convinced the forward-declarations shouldn't be necessary because the body won't be instantiated until the second phase of two-phase lookup. 他确信前向声明不应该是必要的,因为在两阶段查找的第二阶段之前,身体不会被实例化。 He thinks the compiler (gcc 4.6) has a bug. 他认为编译器(gcc 4.6)有一个bug。

I believe the compiler is right because the two foo are different base template functions and the choice of base template needs to be locked-in during the first phase or else you could violate the one-definition rule by instantiating foo before all versions of it have been defined and then again afterwards (consider how the linker assumes that redundant template function definitions are identical, interchangeable, and discardable). 我相信编译器是正确的,因为两个foo不同的基本模板函数,并且在第一阶段需要锁定基本模板的选择,否则你可能通过在它的所有版本之前实例化foo来违反单定义规则已定义,然后再次定义(考虑链接器如何假定冗余模板函数定义相同,可互换和可丢弃)。

So, who is right? 那么,谁是对的?


The above-linked GOTW nicely explains how and why function templates don't partially specialise, but the existence of variadic template functions seems to add to the confusion -- the intuition that foo<int,Tail...> should be a partial specialisation of foo<Head,Tail...> is stronger than that intuition for non-variadic functions, at least to me. 上面链接的GOTW很好地解释了函数模板如何以及为什么不部分专门化,但可变参数模板函数的存在似乎增加了混淆 - foo<int,Tail...>的直觉应该是部分特化foo<Head,Tail...>比非变量函数的直觉强,至少对我而言。

GCC (and Clang) are right. GCC(和Clang)是对的。 MSVC would get it wrong because it does not implement the look-up correctly. MSVC会错误,因为它没有正确实现查找。

There is, it seems, a misunderstanding from your colleague. 似乎是你的同事误解了。 The rules for look-up are: 查找规则是:

  • the Base template function need be declared before it is called from a definition 在从定义调用Base模板函数之前,需要声明它
  • the Specialized template function need be declared before it is instantiated 在实例化之前,需要声明Specialized模板函数

Note: those rules apply for free-functions, within a class no forward declaration is required 注意:这些规则适用于自由函数,在类中不需要前向声明

Note that because a definition also acts as a declaration it is unnecessary, in your example, to forward declare the int version. 请注意,因为定义也充当声明,所以在您的示例中,不必转发声明int版本。

Correct example: 正确的例子:

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

If there is base template available, this is an error. 如果有可用的基本模板,则这是一个错误。 If the specialization is not declared prior to the instantiation, it won't be used, and this may, subsequently, mean a violation of the ODR rule (if another instantiation uses the specialization). 如果在实例化之前未声明特化,则不会使用它,并且这可能随后意味着违反ODR规则(如果另一个实例化使用特化)。

From the Standard (C++0x FDIS): 从标准(C ++ 0x FDIS):

14.6.4.2 14.6.4.2

1. For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that: 1.对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1,3.4.2,3.4.3)找到候选函数,除了:

— For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found. - 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,仅找到模板定义上下文中的函数声明。

— For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found. - 对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明。

If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior. 如果函数名称是非限定id并且调用将是格式错误或者找到更好的匹配,则相关命名空间内的查找会考虑所有在所有翻译单元中的那些名称空间中引入外部链接的函数声明,而不仅仅是考虑在模板定义和模板实例化上下文中找到的那些声明,则程序具有未定义的行为。

Note that the paragraphs referred to are for regular functions. 请注意,所提到的段落适用于常规功能。

Two-phase lookup will find: 两阶段查找将找到:

  • functions which are visible at the point of definition, and 在定义点可见的函数,和
  • functions which can be found by ADL at the point of instantiation. ADL在实例化时可以找到的函数。

template <typename Head, typename... Tail> void foo (Head x, Tail... tail) can't be found by ADL, so if isn't visible at the point of definition it won't be found at all. template <typename Head, typename... Tail> void foo (Head x, Tail... tail)在ADL中找不到,所以如果在定义时看不到它就根本找不到。

In other words, GCC is right. 换句话说,海湾合作委员会是对的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM