为什么 ADL 不能使用 std::get 解析为正确的函数

Why ADL does not resolve to the correct function with std::get

本文关键字:函数 get 不能 ADL std 为什么      更新时间:2023-10-16

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

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

问题最终是模板:

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

非限定 idget在类成员访问查找的E范围内查找,如果找到至少一个声明,则初始值设定项e.get<i>()。否则,初始值设定项为get<i>(e),其中get在关联的命名空间中查找。在任一情况下,get<i>都被解释为模板 ID。[ 注:不执行普通非限定查找。

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

参数相关查找对于给定显式模板参数的函数模板的工作方式不同。

尽管函数调用可以通过 ADL 解决,即使普通 查找什么也没找到,函数调用函数模板 显式指定的模板参数要求存在 通过普通查找找到的模板的声明(否则为 遇到未知名称后跟小于的语法错误 字符(

基本上,非限定查找需要某种方法来查找模板函数。然后,ADL 可以启动(因为名称get然后已知是模板(。Cpp首选项举了一个例子:

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 循环执行的非成员函数的查找开始和结束
  • 从模板实例化点开始的依赖名称查找。
  • 非成员函数的查找通过类元组类型的结构化绑定声明执行

强调添加

快进到 C++20

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

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