前向声明模板函数的必要性 [英] Necessity of forward-declaring template functions

查看:534
本文介绍了前向声明模板函数的必要性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近创建了这个示例代码来说明C ++ 11变量模板函数的用法。

  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)); // Printsint:123?:123.0

如果前两行code> foo ,那么它将打印 int:123int:123 。这让一个经验丰富,知识渊博的C ++程序员感到惊讶。



他相信前瞻性声明不是必要的,因为身体不会被实例化,直到第二阶段两相查找。他认为编译器(gcc 4.6)有一个错误。



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

那么,谁是对的?






上面链接的GOTW很好地解释了为什么函数模板没有部分专门化,但是可变参数模板函数的存在似乎增加了混淆 - foo< int,Tail。 ..> 应该是 foo< Head,Tail ...> 的部分专门化比非可变函数,至少对我。

解决方案

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



似乎是,你的同事的误解。查找规则为:




  • 基本模板函数需要在定义调用之前声明

  • 专业模板函数需要在实例化之前声明



注意:



请注意,因为定义也作为声明,因此在您的示例中不需要转发声明 int 版本。



正确的示例:

  template< typename T> void foo(T); // declare foo< T> 

template< typename T> void bar(T t){foo(t); } // call foo< T> (相关上下文)

模板<> void foo< int>(int); // declare specialiaztion foo< int>

void bar(int i){foo(i); } // instantiate foo< T>与int
//这是专业化

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



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


14.6.4.2 p>

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



- 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)



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



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


请注意,引用的段落用于常规功能。


I recently created this example code to illustrate C++11 variadic template function usage.

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. This surprised a certain experienced and knowledgeable C++ programmer.

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.

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).

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.

解决方案

GCC (and Clang) are right. MSVC would get it wrong because it does not implement the look-up correctly.

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
  • the Specialized template function need be declared before it is instantiated

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.

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).

From the Standard (C++0x FDIS):

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:

— 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.

— 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.

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.

Note that the paragraphs referred to are for regular functions.

这篇关于前向声明模板函数的必要性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆