从元组中获取元素

fre*_*low 23 c++ templates tuples argument-dependent-lookup c++11

可能重复:
为什么ADL没有找到功能模板?

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

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

g ++ 4.6.0说:

error: 'get' was not declared in this scope
Run Code Online (Sandbox Code Playgroud)

Visual Studio 2010说:

error C2065: 'get': undeclared identifier
Run Code Online (Sandbox Code Playgroud)

为什么?

Naw*_*waz 22

这是因为您尝试通过提供模板参数来显式实例化get函数模板0.对于模板,如果在调用点处显示具有该名称的函数模板,则ADL起作用.这个可见的函数模板只能帮助触发 ADL(它可能不会被实际使用),然后,可以在其他命名空间中找到最佳匹配.

请注意,触发(或启用)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
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

- 末端示例] - 尾注]

  • 非常好的答案.从圣书中引用,我的意思是标准,会使它成为一个完美的答案 (2认同)

Pot*_*ter 13

ADL不直接应用于模板IDget<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
Run Code Online (Sandbox Code Playgroud)

http://ideone.com/fb8Ai

注意,not_used_for_anything上面仅仅是一种安全机制.它的目的是一个永不完整的不完整类型.省略它也可以,但是不安全,因为它可能会与您可能想要的签名冲突.

template< typename >
void get() = delete;
Run Code Online (Sandbox Code Playgroud)

http://ideone.com/WwF2y

注意:标准的上述引用是非规范性的,这意味着委员会认为,我们能够在没有解释的情况下弄清楚这一点,因为它隐含在语言和语法的其余部分,特别是3.4的事实. .2没有说明查找模板ID.是的,对!

  • `using std :: get;`也适用于在范围内引入名为`get`的函数模板. (3认同)