2014-05-22 21 views
3

当编译和运行代码两相名称查找:POD中与自定义类型

#include <iostream> 

struct A { A(int){} }; 

void foo(int) { std::cout << "foo(int)" << std::endl; } 

template< typename T > 
struct S { 
    void bar() { foo(5); } 
    void foobar() { T t = 5; foo(t); } 
}; 

inline void foo(A) { std::cout << "foo(A)" << std::endl; } 
inline void foo(double) { std::cout << "foo(double)" << std::endl; } 

int main(int argc, char* argv[]) 
{ 
    S<double> s0; 
    s0.bar(); 
    s0.foobar(); 

    std::cout << '\n'; 

    S<A> s1; 
    s1.bar(); 
    s1.foobar(); 
} 

我得到的输出(使用克++ 4.8,铛++ 3.2或ICPC 13.1)

foo(int) 
foo(int) 

foo(int) 
foo(A) 

虽然最后两行对于考虑两阶段查找规则的我来说非常有意义,我预计前两行为foo(int) foo(double)

看来,在这种情况下,对于foobar()调用foo()在实例化之前得到查询,这应该是不可能的。任何提示?

回答

5

由于T是模板参数,表达t是类型依赖性的并且因而foo是一个从属名称在函数调用foo(t)。 [temp.dep.candidate] 14.6.4.2/1说:

对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1,3.4.2, 3.4.3)不同之处在于:

  • 对于使用不合格的名称查找(3.4.1)或限定名查找(3.4.3)查找的部分,只从模板定义上下文函数声明被发现。

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

如果函数名是不合格-ID并会形成不良的通话或会找到更好的比赛不得不考虑所有的函数声明与这些引进外部链接相关的命名空间内查找所有翻译单元中的名称空间,不仅考虑在模板定义和模板实例化上下文中找到的那些声明,那么该程序具有未定义的行为。

当实例S<double>::foobarT显然是double,也没有相关的命名空间。因此,foo的唯一声明将是第一个项目符号中描述的模板定义上下文(void foo(int))中的那些声明。

当实例化S<A>::foobar时,TA。来自定义背景的foo声明

  • void foo(int)

    从关联的命名空间A的(全局命名空间)中发现:

  • inline void foo(A) { std::cout << "foo(A)" << std::endl; }

  • inline void foo(double) { std::cout << "foo(double)" << std::endl; }

显然void foo(A)是最好的匹配。

5

年代以前只要定义FOO(双):

#include <iostream> 

struct A { A(int){} }; 

void foo(int) { std::cout << "foo(int)" << std::endl; } 
inline void foo(double) { std::cout << "foo(double)" << std::endl; } //here 
inline void foo(A) { std::cout << "foo(A)" << std::endl; } 

template< typename T > 
struct S { 
    void bar() { foo(5); } 
    void foobar() { T t = 5; foo(t); } 
}; 

int main(int argc, char* argv[]) 
{ 
    S<double> s0; 
    s0.bar(); 
    s0.foobar(); 

    std::cout << '\n'; 

    S<A> s1; 
    s1.bar(); 
    s1.foobar(); 
} 

输出:

FOO(INT)FOO(双)

FOO(INT)FOO(A)

编辑: 所以S :: foo是一个非依赖名称,并且在模板te被定义为 而S :: foobar是一个从属名称,并且在模板实例化时被解析。

这就是为什么s1.foobar可以打印A(因为FOO(A)在这一点定义)

+1

我认为这实际上是强制性的。 IIRC,如果查找'foo'在使用点和实例化点上给出不同的结果,那么就是UB,不需要诊断。 – MSalters

+0

当然,我可以更改代码并得到不同的结果,但问题是,为什么上面的代码正常工作。 – proto

+0

'foo'没有依赖于模板参数的参数,所以'foo'声明必须可用',所以不能同时解析S :: foo函数(非依赖名称) – Kiroxas

相关问题