16

我正在尝试编写一个模板函数,该函数使用解析get为获取结构/范围(tuple-esque)的成员的 ADL。

#include <iostream>
#include <utility>
#include <tuple>

int main() {
    auto tup = std::make_tuple(1, 2);
    std::cout << get<0>(tup) << std::endl;
}

我这样做是因为结构化绑定提案(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf §11.5.3)说明了如何get使用从结构中获取元素。它说非成员get用于从结构中获取元素。

我假设上面的代码会编译,因为 ADL 会导致get在命名空间中查找函数std(因为它的参数类型是std::tuple<int, int>,它是 in std),在那里可以找到它。但是,我得到一个错误。有人可以在这里解释正确的方法以及为什么上面的代码不起作用吗?在这种情况下如何强制 ADL 发生?

4

3 回答 3

15

问题最终是模板:

std::cout << get<0>(tup) << std::endl;
//           ~~~~

那时,编译器还不知道这是一个需要使用 ADL 查找的函数 -get只是一个名称。而且由于该名称本身找不到任何内容,因此这将被解释为一个未知名称,后跟小于。要使其正常工作,您需要一些其他get可见的功能模板:

using std::get;
std::cout << get<0>(tup) << std::endl; // now, OK

即使它什么都不做:

template <class T> void get();

int main() {
    auto tup = std::make_tuple(1, 2); 
    std::cout << get<0>(tup) << std::endl;
}

结构化绑定措辞get使用参数相关查找显式查找,因此它避免需要具有get来自 [dcl.struct.bind] 的已经可见的函数模板命名:

unqualified-id 在按类成员访问查找get的范围内E查找,如果找到至少一个声明,则初始化程序为e.get<i>(). 否则,初始化程序是get<i>(e),在get关联的命名空间中查找。在任何一种情况下,get<i>都被解释为template-id。[注:不执行普通的非限定查找。——尾注]

笔记是关键。如果我们执行了不合格的查找,我们就会失败。

于 2017-08-03T20:27:54.893 回答
12

Argument Dependent Lookup对于给出显式模板参数的函数模板的工作方式不同。

尽管即使普通查找什么也找不到,也可以通过 ADL 解析函数调用,但是对具有显式指定模板参数的函数模板的函数调用需要有普通查找找到的模板的声明(否则,它是语法错误遇到未知名称后跟小于字符)

基本上,不合格的查找需要有某种方法来找到模板函数。然后,ADL 可以启动(因为该名称get被称为模板)。Cppreference 举了一个例子:

namespace N1 {
  struct S {};
  template<int X> void f(S);
}
namespace N2 {
  template<class T> void f(T t);
}
void g(N1::S s) {
  f<3>(s);      // Syntax error (unqualified lookup finds no f)
  N1::f<3>(s);  // OK, qualified lookup finds the template 'f'
  N2::f<3>(s);  // Error: N2::f does not take a non-type parameter
                //        N1::f is not looked up because ADL only works
                //              with unqualified names
  using N2::f;
  f<3>(s); // OK: Unqualified lookup now finds N2::f
           //     then ADL kicks in because this name is unqualified
           //     and finds N1::f
}

结构化绑定是一种特殊情况,启用了 ADL。

在以下上下文中,仅 ADL 查找(即仅在关联命名空间中查找)发生:

  • 如果成员查找失败,则由 range-for 循环执行非成员函数的查找开始和结束
  • 从模板实例化的角度查找依赖名称。
  • 非成员函数的查找由类元组类型的结构化绑定声明执行

强调添加

于 2017-08-03T20:26:59.137 回答
0

快进到 C++20

已被 C++20 接受的p0846r0现在允许 ADL 使用显式模板参数调用模板函数。

因此,OP 的代码现在可以按原样使用 C++20 进行编译,而不会出现错误!

于 2020-09-10T13:28:36.753 回答