23

Possible Duplicate:
Why doesn't ADL find function templates?

Calling get does not seem to invoke argument dependent lookup:

auto t = std::make_tuple(false, false, true);
bool a = get<0>(t);        // error
bool b = std::get<0>(t);   // okay

g++ 4.6.0 says:

error: 'get' was not declared in this scope

Visual Studio 2010 says:

error C2065: 'get': undeclared identifier

Why?

Community
  • 1
  • 1
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • You may want to read https://groups.google.com/group/comp.lang.c++.moderated/msg/dc5169c6ff1ea849?hl=de and the associated discussion. The guy posted another post afterwards about his insights: https://groups.google.com/group/comp.std.c++/browse_thread/thread/432ac0570724c75a/12cc1951eadd084e? – Johannes Schaub - litb Sep 11 '11 at 10:05
  • @Troubadour Possible duplicate of a question edited by the OP ;) – Dan Sep 11 '11 at 13:29
  • @Dan: What a coincidence :) That was just a blind re-tagging action from [tag:adl] to [tag:argument-dependent-lookup], though. – fredoverflow Sep 11 '11 at 14:14

2 Answers2

22

It's because you attempt to explicitly instantiate get function template, by providing 0 as template argument. In case of templates, ADL works if a function template with that name is visible at the point of the call. This visible function template only helps triggering ADL (it may not be used actually) and then, a best matching can be found in other namespaces.

Note that the function template which triggers (or enable) ADL, need not to have definition:

namespace M
{
    struct S{};

    template<int N, typename T>
    void get(T) {}     
}

namespace N
{
   template<typename T>
   void get(T); //no need to provide definition
                // as far as enabling ADL is concerned!
} 

void f(M::S s)
{
   get<0>(s); //doesn't work - name `get` is not visible here 
}

void g(M::S s)
{
   using N::get; //enable ADL
   get<0>(s); //calls M::get
}

In g(), the name N::get triggers ADL when calling get<0>(s).

Demo : http://ideone.com/83WOW


C++ (2003) section §14.8.1/6 reads,

[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]

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 2
    very good answer. a quote from the holy book, I mean standard, would make it a perfect answer – Armen Tsirunyan Sep 11 '11 at 08:27
  • Nawaz, can you give some quote from c++ 03 standard(14882) ? – RoundPi Sep 11 '11 at 09:33
  • @Armen: Done. But even before me, Potatoswatter already did that. Since he didn't quote the example, I did it along with the quotation. – Nawaz Sep 11 '11 at 09:35
  • @GoB00st: I've already done that. Refresh your page. – Nawaz Sep 11 '11 at 09:36
  • I would give this +1 if it would say what the spec says, but unfortunately it contradicts with the spec quote provided. You say "Please note that it is the explicit instantiation of template which fails" and "In case of templates, ADL works if the template is instantiated implicitly, by template argument deduction." but his use of syntax does not refer to the `get` function template and is not a function call at all. As the Standard example shows, `get<0>(x)` *is* capable of doing ADL. – Johannes Schaub - litb Sep 11 '11 at 09:41
  • @Johannes: Yes. I was wrong at that conclusion. Anyway, I edited my post. See if it's correct now? – Nawaz Sep 11 '11 at 10:03
13

ADL doesn't directly apply to template-id's such as get<0>, so the compiler doesn't really get started down that path. C++11 §14.8.1/8 (in C++03, 14.8.1/6):

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

It goes on to give a short example. So the workaround is quite easy:

#include <tuple>

template< typename > // BEGIN STUPID BUT HARMLESS HACK
void get( struct not_used_for_anything ); // END STUPIDITY

auto t = std::make_tuple(false, false, true);
bool a = get<0>(t);        // Now the compiler knows to use ADL!
bool b = std::get<0>(t);   // okay

http://ideone.com/fb8Ai

Note that the not_used_for_anything in the above is merely a safety mechanism. It's intended to be an incomplete type which is never completed. Omitting it works as well, but is unsafe because it could collide with a signature you might want.

template< typename >
void get() = delete;

http://ideone.com/WwF2y

Note: the above quote from the Standard is non-normative, meaning that in the opinion of the Committee, we would be able to figure this out without explanation, because it's implied by the rest of the language and grammar, particularly the fact that 3.4.2 says nothing about looking up template-ids. Yeah, right!

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 3
    `using std::get;` also works for introducing a function template named `get` in scope. – Luc Danton Sep 11 '11 at 10:13
  • Excellent explanation of a horrible wart. Also the technique of declaring `struct not_used_for_anything` is a new one on me -- I thought this was not allowed, because there are frequent injunctions in the standard against defining `struct` types where the grammar would otherwise allow them to be used (e.g. as the argument to `sizeof`) -- but I see now that while in-place *definition* is indeed forbidden, simply naming a type that hasn't been defined or declared yet is kosher if the context allows an incomplete type (as function parameters do). – j_random_hacker Sep 11 '11 at 10:23
  • 1
    @j_random: Yep, you can even declare a class inside a template argument! – Potatoswatter Sep 11 '11 at 11:37
  • 1
    Hmm, a `= delete` is a function definition. The spec forbids parameter types to be incomplete for function definitions. I wonder whether this is a defect in the spec or whether it was intended to apply to `= delete` actually. – Johannes Schaub - litb Sep 11 '11 at 11:59
  • Don't know, but it's impossible to form a call to the function anyway, so I'll remove the `delete`. – Potatoswatter Sep 11 '11 at 14:04
  • @Luc Danton: but note that even if you do `using std::get;`, the compiler will (for example) _still_ select `boost::fusion::tuple::get` if your argument is a `boost::fusion::tuple<...>` - this might not be what you expect when you put the using line in (it tripped me up writing a small header-only lib) – sehe Sep 21 '11 at 09:52