26

Consider the following C++ program:

#include <memory>

struct A {};

struct B : A {};

int main()
{
    auto x = std::make_shared<A>();
    if (auto p = dynamic_pointer_cast<B>(x));
}

When compiling with MSVC 2010, I obtain the following error:

error C2065: 'dynamic_pointer_cast' : undeclared identifier

The error persists if auto is replaced by std::shared_ptr<A>. When I fully qualify the call with std::dynamic_pointer_cast, the program successfully compiles.

Also, gcc 4.5.1 doesn't like it either:

error: 'dynamic_pointer_cast' was not declared in this scope

I thought that std::dynamic_pointer_cast would have been picked by Koenig lookup, since the type of x lives in the std namespace. What am I missing here ?

Nawaz
  • 353,942
  • 115
  • 666
  • 851
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197

2 Answers2

24

I think section §14.8.1/6 (C++03, and I think it holds in C++11 also) applies to this case which reads as,

[Note: For simple function names, argument dependent lookup (3.4.2) applies even when the function name is not visible within the scope of the call. This is because the call still has the syntactic form of a function call (3.4.1). But when a function template with explicit template arguments is used, the call does not have the correct syntactic form unless there is a function template with that name visible at the point of the call. If no such name is visible, the call is not syntactically well-formed and argument-dependent lookup does not apply. If some such name is visible, argument dependent lookup applies and additional function templates may be found in other namespaces.

[Example:

namespace A {
     struct B { };
     template<int X> void f(B);
}
namespace C {
     template<class T> void f(T t);
}
void g(A::B b) {
     f<3>(b);    //ill-formed: not a function call
     A::f<3>(b); //well-formed
     C::f<3>(b); //ill-formed; argument dependent lookup
                 // applies only to unqualified names

    using C::f;
     f<3>(b); //well-formed because C::f is visible; then
              // A::f is found by argument dependent lookup
}

—end example] —end note]

Your case do not trigger ADL because you explicitly pass template argument and there is no template with the same name available at the site where you call dynamic_pointer_cast.

One trick to enable ADL is to add a dummy template with same name to your code, as shown below:

#include <memory>

struct A {};

struct B : A {};

template<int> //template parameter could be anything!
void dynamic_pointer_cast(); //ADD this. NO NEED TO DEFINE IT

int main()
{
   auto x = std::make_shared<A>();
   if (auto p = dynamic_pointer_cast<B>(x)); //now it should work through ADL
}
Nawaz
  • 353,942
  • 115
  • 666
  • 851
18

Koenig lookup only applies to finding functions. Here, you first have to find a template, then instantiate it, before you have a function. In order to simply parse the code, the compiler has to know that dynamic_pointer_cast is a template (otherwise, '<' is less than, and not the start of a template argument list); until the compiler knows that dynamic_pointer_cast is a function template, it doesn't even know that a function call is involved. The expression it sees is basically a < b > c, where < and > are the relational operators.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • It seems that Koenig lookup can find function templates too, cf. Nawaz's answer. Nonetheless, your argument is the right one. – Alexandre C. Mar 23 '12 at 12:11
  • Pedantically speaking, Koenig lookup applies to function template as well. It just doesn't work when you explicitly pass template argument and the template name isn't visible at the site of the call. In this case, it doesn't work because the call doesn't have correct function-call syntax, as the compiler doesn't know that the name is actually a name of function template. – Nawaz Mar 23 '12 at 12:23
  • 5
    @Nawaz In the end, it doesn't work because the standard says it doesn't, and there are several reasons why the standard doesn't say make it work. Both the one you give and what I explained (which comes from discussions in the committee, many, many years back) go back to the same basic problem: in order to correctly parse the expression, the compiler has to know that the symbol is a template, and in order to know that, it has to look it up. And it can't use Koenig lookup looking it up, because it hasn't yet parsed enough to know there is a function call. – James Kanze Mar 23 '12 at 13:54
  • @JamesKanze: Ohh.. I agree with that, then. – Nawaz Mar 23 '12 at 13:56