简体   繁体   中英

Two-phase name lookup: PODs vs. custom types

When compiling and running the code

#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();
}

I get the output (using g++ 4.8, clang++ 3.2 or icpc 13.1)

foo(int)
foo(int)

foo(int)
foo(A)

While the last two lines make perfect sense to me considering the two-phase lookup rules, I would expect foo(int) foo(double) for the first two lines.

It seems that in this case for the foobar() call foo() gets looked up before instantiation which should not be possible. Any hints?

Just define foo(double) before S :

#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();
}

output :

foo(int) foo(double)

foo(int) foo(A)

EDIT : So S::foo is a non-dependent name and is resolved when the template is defined and S::foobar is a dependant name and is resolved when when the template is instantiated.

This is why s1.foobar can print A (because foo(A) is defined at this point)

Since T is a template parameter, the expression t is type-dependent and hence foo is a dependent name in the function call foo(t) . [temp.dep.candidate] 14.6.4.2/1 says:

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.

When instantiating S<double>::foobar , T is obviously double and has no associated namespaces. So the only declarations of foo that will be found are those from the template definition context ( void foo(int) ) as described in the first bullet.

When instantiating S<A>::foobar , T is A . Declarations of foo from both the definition context

  • void foo(int)

    and from A 's associated namespace (the global namespace) are found:

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

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

Clearly void foo(A) is the best match.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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