在C ++模板实例化中查找依赖名称 [英] Lookup of dependent names in C++ template instantiation

查看:895
本文介绍了在C ++模板实例化中查找依赖名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我尝试编译此代码

  // void foobar 

template< class T>
struct Foo {
void bar(T t){foobar(t); };
};

void foobar(int);

模板类Foo< int> ;;

与g ++ 4.8.2我得到以下错误信息

  foo.cc:在实例化'void Foo< T> :: bar(T)[with T = int]':
foo.cc:10 :16:从这里需要
foo.cc:5:27:错误:'foobar'没有在此范围中声明,没有
声明通过参数依赖查找找到
实例化点[-fpermissive]
void bar(T t){foobar(t); };
^
foo.cc:8:6:注意:'void foobar(int)'在这里声明,稍后在翻译单元
void foobar(int);
^

(与clang 3.4几乎相同)。



首先,我认为代码是正确的,并且应该编译,因为foobar是模板声明中的依赖名称,在模板被实例化时在阶段二。当在最后一行执行时,'foobar(int)'已经被声明。代码编译,BTW,当我取消注释最顶行,但两个声明都在实例化之前,所以这没有关系。



其次,错误消息本身似乎矛盾对我来说。它说:在instatiation的时候没有发现声明,这是foo.cc:10:16,它说它在foo.cc:8:6被声明为后来。对于所有我知道的数字和英语语言,我会称之为之前而不是稍后。



所以,这是一个在gcc中的错误,有问题?因为这似乎是一个常见的使用模式,我不能完全相信,但是。



BTW:当我尝试第二个例子名称解析依赖类型(位于MSDN)( http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx )与g ++的结果不同于vc ++,这(通常不是,但在这个特定的情况下)会破坏这是一个bug在g ++。

解决方案

tl; dr Foo< int> 不调用任何ADL,但 Foo 将(其中 X 是类类型)。






首先,在此代码 foobar 名称因为(C ++ 14 / N3936) [temp.dep] / 1


在以下形式的表达式中:

  postfix-expression(expression-list opt)

其中postfix-expression是一个非限定id,unqualified-id表示一个依赖名称if [...] >


  • expression-list中的任何表达式是类型依赖表达式(14.6.2.2),或


t 是一个依赖名称,因为它是声明的一部分 T t 其中 T 是模板参数,因此是依赖类型。



移动到依赖名称解析,有 [temp.dep.res] / 1 ,它介绍了可以在定义上下文中查找名称的事实,实例化上下文,并且定义实例化上下文的位置。为简洁起见,我省略了,但是在这个例子中模板类Foo< int> ;; 是实例化的点。



下一位是 [temp.dep.candidate] / 1


对于其中 postfix-expression 是依赖名称的函数调用,使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了:




  • 对于使用非限定名称查找(3.4.1)的查找部分,只能找到来自模板定义上下文的函数声明。

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


最后两部分是两阶段查找的两个阶段。 (注意 - 这一部分的改变从C ++ 11到C ++ 14,但效果在同一个)。



在第一阶段,3.4.1 ,未找到 foobar 的名称。






所以我们进入第二阶段。名称的实际位置按照3.4.2中所述进行查找。文本很长,但这里有两个相关规则:



  • 如果T是


  • 如果T是类类型(包括联合),其关联的类是:类本身;它是其成员的类,如果有的话;及其直接和间接基类。其关联的命名空间是其关联类的最内层的命名空间。 [...]



Foo ,那么查找的第二阶段不会引入任何额外的命名空间来搜索。



但是,如果您将示例更改为 struct X {}; ,然后更改 int X 无处不在,然后代码 这是因为后面的项目符号点:类型类型的参数的ADL确实搜索该类的封闭命名空间(现在是全局命名空间),但内置类型的参数的ADL不搜索全局命名空间。


When I try to compile this code

// void foobar(int); 

template <class T>
struct Foo {
  void bar(T t) { foobar(t); };
};

void foobar(int);

template class Foo<int>;

with g++ 4.8.2 I get the following error message

foo.cc: In instantiation of ‘void Foo<T>::bar(T) [with T = int]’:
foo.cc:10:16:   required from here
foo.cc:5:27: error: ‘foobar’ was not declared in this scope, and no 
             declarations were found by argument-dependent lookup at 
               the point of instantiation [-fpermissive]
   void bar(T t) { foobar(t); };
                           ^
foo.cc:8:6: note: ‘void foobar(int)’ declared here, later in the translation unit
 void foobar(int);
      ^

(With clang 3.4 it's nearly the same).

First, I think the code is correct and should compile, since foobar is a dependent name in the template declaration and should be looked up only in phase two when the template is instantiated. When this is done in the last line, 'foobar(int)' is already declared. The code compiles, BTW, when I uncomment the topmost line, but both declarations are before the instantiation and so this should not matter.

Second, the error message itself seems contradictory to me. It says "no declarations were found at the point of instatiation" which is foo.cc:10:16, and it says it's declared "later" at foo.cc:8:6. For all that I know about numbers and the English language I would call that "before" not "later".

So, is it a bug in gcc or did I get something wrong? Since this seems to me a common usage pattern I can't quite believe that, however.

BTW: when I try out the second example of "Name Resolution for Dependent Types" at MSDN (http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx) with g++ the result is different from vc++, which (not generally, but in this specific case) would undermine this being a bug in g++.

解决方案

tl;dr Foo<int> doesn't invoke any ADL, but Foo<X> would (where X is a class type).


First of all, in this code foobar is a dependent name because of (C++14/N3936) [temp.dep]/1

In an expression of the form:

postfix-expression ( expression-list opt )

where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if [...]

  • any of the expressions in the expression-list is a type-dependent expression (14.6.2.2), or

and t is a dependent name because it is part of a declaration T t where T is a template parameter and thus a dependent type.

Moving onto dependent name resolution, there is [temp.dep.res]/1 which introduces the fact that names can be both looked up in the definition context, and the instantiation context, and defines where the instantiation context is. I have omitted that for brevity, but in this example template class Foo<int>; is the point of instantiation.

The next bit is [temp.dep.candidate]/1:

For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:

  • For the part of the lookup using unqualified name lookup (3.4.1), 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.

Those last two parts are the "two phases" of two-phase lookup. (Note - this section changed in wording from C++11 to C++14, but the effect in the same).

In the first phase, 3.4.1, no names are found for foobar.


So we move onto the second phase. The actual places that names are looked up as described in 3.4.2. The text is long but the here are two of the relevant rules:

  • If T is a fundamental type, its associated sets of namespaces and classes are both empty.

  • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. [...]

So when you instantiate Foo<int>, then the second phase of lookup does not introduce any additional namespaces to search.

However, if you change your example to have struct X {}; and then change int to X everywhere, then the code does compile. This is because of the latter bullet point: ADL for an argument of class type does search the enclosing namespace of that class (which is the global namespace now), however ADL for an argument of built-in type does not search the global namespace.

这篇关于在C ++模板实例化中查找依赖名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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