23

可能重复:
为什么 ADL 找不到函数模板?

调用get似乎不会调用依赖于参数的查找:

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 说:

error: 'get' was not declared in this scope

Visual Studio 2010 说:

error C2065: 'get': undeclared identifier

为什么?

4

2 回答 2

22

这是因为您尝试通过提供模板参数来显式实例化函数模板。在模板的情况下,如果具有该名称的函数模板在调用点可见,则 ADL 工作。这个可见的函数模板只帮助触发ADL(它可能实际上没有使用),然后,可以在其他命名空间中找到最佳匹配。get0

请注意,触发(或启用)ADL的功能模板不需要定义

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
}

其中g(),名称N::get在调用时触发 ADL get<0>(s)

演示:http: //ideone.com/83WOW


C++(2003)第 14.8.1/6 节读取,

[注意:对于简单的函数名称,即使函数名称在调用范围内不可见,参数相关查找 (3.4.2) 也适用。这是因为调用仍然具有函数调用 (3.4.1) 的语法形式。但是,当使用具有显式模板参数的函数模板时,调用不具有正确的语法形式,除非在调用点存在具有该名称的函数模板可见。如果没有这样的名称可见,则该调用在语法上不是格式正确的,并且不适用依赖于参数的查找。如果某些此类名称可见,则应用依赖于参数的查找,并且可以在其他名称空间中找到其他函数模板。

[例子:

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
}

—结束示例] —结束注释]

于 2011-09-11T08:16:12.710 回答
13

ADL 并不直接应用于template-id的诸如get<0>,因此编译器并没有真正开始沿着这条路走下去。C++11 §14.8.1/8(在 C++03 中,14.8.1/6):

[注意:对于简单的函数名称,即使函数名称在调用范围内不可见,参数相关查找(3.4.2)也适用。这是因为调用仍然具有函数调用 (3.4.1) 的语法形式。但是,当使用具有显式模板参数的函数模板时,调用不具有正确的语法形式,除非在调用点存在具有该名称的函数模板可见。如果没有这样的名称可见,则该调用在语法上不是格式正确的,并且不适用依赖于参数的查找。如果某些此类名称可见,则应用依赖于参数的查找,并且可以在其他名称空间中找到其他函数模板。

它继续给出一个简短的例子。所以解决方法很简单:

#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!

于 2011-09-11T09:15:35.280 回答