【问题标题】:getting an element from a tuple [duplicate]从元组中获取元素[重复]
【发布时间】:2011-11-14 16:12:18
【问题描述】:

可能重复:
Why doesn't ADL find function templates?

调用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

为什么?

【问题讨论】:

标签: c++ templates tuples c++11 argument-dependent-lookup


【解决方案1】:

这是因为您尝试显式通过提供0 作为模板参数来实例化get 函数模板。在模板的情况下,如果具有该名称的函数模板在调用点可见,则 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
}

g() 中,名称N::get 在调用get&lt;0&gt;(s) 时会触发ADL。

演示: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
}

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

【讨论】:

  • 很好的答案。引用圣书,我的意思是标准,将使它成为一个完美的答案
  • Nawaz,你能引用 c++ 03 标准(14882)的一些话吗?
  • @Armen:完成。但即使在我之前,Potatoswatter 已经这样做了。由于他没有引用示例,所以我将其与引用一起做了。
  • @GoB00st:我已经这样做了。刷新您的页面。
  • 如果它能说出规范所说的内容,我会给出 +1,但不幸的是,它与提供的规范报价相矛盾。您说“请注意,模板的显式实例化失败”和“在模板的情况下,如果模板是通过模板参数推导隐式实例化的,ADL 就可以工作。”但他对语法的使用并没有引用get 函数模板,根本不是函数调用。正如标准示例所示,get&lt;0&gt;(x) 能够进行 ADL。
【解决方案2】:

ADL 并不直接应用于 template-id,例如 get&lt;0&gt;,因此编译器并没有真正开始沿着这条路走下去。 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

请注意,上面的not_used_for_anything 只是一种安全机制。它旨在成为一种从未完成的不完整类型。省略它也可以,但不安全,因为它可能与您可能想要的签名发生冲突。

template< typename >
void get() = delete;

http://ideone.com/WwF2y

注意:标准中的上述引用是非规范性的,这意味着在委员会看来,我们无需解释就可以解决这个问题,因为其他语言和语法都暗示了这一点,尤其是事实上,3.4.2 没有说明查找模板 ID。对对对!

【讨论】:

  • using std::get; 也适用于在作用域内引入名为get 的函数模板。
  • 对可怕的疣的出色解释。此外,声明struct not_used_for_anything 的技术对我来说是一种新的技术——我认为这是不允许的,因为标准中经常有禁止定义struct 类型的禁令,否则语法会允许它们被使用(例如sizeof 的参数)——但我现在看到,虽然确实禁止就地 definition,但如果上下文允许,简单地命名尚未定义或声明的类型是犹太教不完整类型(与函数参数一样)。
  • @j_random:是的,你甚至可以在模板参数中声明一个类!
  • 嗯,= delete 是一个函数定义。该规范禁止函数定义的参数类型不完整。我想知道这是否是规范中的缺陷,或者它是否打算实际应用于= delete
  • 不知道,反正也不可能形成对函数的调用,所以我把delete去掉。
猜你喜欢
  • 2011-07-30
  • 2016-09-09
  • 2011-04-04
  • 2011-11-01
  • 2020-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-25
相关资源
最近更新 更多