为什么 ADL 无法使用 std::get 解析为正确的函数
Why ADL does not resolve to the correct function with std::get
我正在尝试编写一个模板函数,该函数使用已解析的 ADL get
来获取 struct/range(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 发生?
参数相关查找的工作方式不同 for function templates where an explicit template argument is given。
Although a function call can be resolved through ADL even if ordinary
lookup finds nothing, a function call to a function template with
explicitly-specified template arguments requires that there is a
declaration of the template found by ordinary lookup (otherwise, it is
a syntax error to encounter an unknown name followed by a less-than
character)
基本上,非限定查找需要某种方式来找到模板函数。然后,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。
In the following contexts ADL-only lookup (that is, lookup in
associated namespaces only) takes place:
- the lookup of non-member functions begin and end performed by the range-for loop if member lookup fails
- the dependent name lookup from the point of template instantiation.
- the lookup of non-member function get performed by structured binding declaration for tuple-like types
已强调
问题最终是模板:
std::cout << get<0>(tup) << std::endl;
// ~~~~
此时,编译器还不知道这是一个需要使用 ADL 查找的函数 - get
只是一个名称。由于该名称本身找不到任何内容,因此这将被解释为未知名称后跟小于号。要让它工作,你需要一些其他函数模板 get
visible:
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
,因此它避免了从 [dcl.struct.bind 中使用名为 get
的已经可见的函数模板的需要]:
The unqualified-id get
is looked up in the scope of E
by class member access lookup, and if that finds at least one declaration, the initializer is e.get<i>()
. Otherwise, the initializer is get<i>(e)
, where get
is looked up in the associated namespaces. In either case, get<i>
is interpreted as a template-id. [ Note: Ordinary unqualified lookup is not performed. — end note ]
笔记是关键。如果我们执行了不合格的查找,我们就会失败。
快进到 C++20
p0846r0 已被 C++20 接受,现在允许 ADL 使用显式模板参数调用模板函数。
所以 OP 的代码 now compiles as is with C++20 没有错误!
我正在尝试编写一个模板函数,该函数使用已解析的 ADL get
来获取 struct/range(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 发生?
参数相关查找的工作方式不同 for function templates where an explicit template argument is given。
Although a function call can be resolved through ADL even if ordinary lookup finds nothing, a function call to a function template with explicitly-specified template arguments requires that there is a declaration of the template found by ordinary lookup (otherwise, it is a syntax error to encounter an unknown name followed by a less-than character)
基本上,非限定查找需要某种方式来找到模板函数。然后,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。
In the following contexts ADL-only lookup (that is, lookup in associated namespaces only) takes place:
- the lookup of non-member functions begin and end performed by the range-for loop if member lookup fails
- the dependent name lookup from the point of template instantiation.
- the lookup of non-member function get performed by structured binding declaration for tuple-like types
已强调
问题最终是模板:
std::cout << get<0>(tup) << std::endl;
// ~~~~
此时,编译器还不知道这是一个需要使用 ADL 查找的函数 - get
只是一个名称。由于该名称本身找不到任何内容,因此这将被解释为未知名称后跟小于号。要让它工作,你需要一些其他函数模板 get
visible:
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
,因此它避免了从 [dcl.struct.bind 中使用名为 get
的已经可见的函数模板的需要]:
The unqualified-id
get
is looked up in the scope ofE
by class member access lookup, and if that finds at least one declaration, the initializer ise.get<i>()
. Otherwise, the initializer isget<i>(e)
, whereget
is looked up in the associated namespaces. In either case,get<i>
is interpreted as a template-id. [ Note: Ordinary unqualified lookup is not performed. — end note ]
笔记是关键。如果我们执行了不合格的查找,我们就会失败。
快进到 C++20
p0846r0 已被 C++20 接受,现在允许 ADL 使用显式模板参数调用模板函数。
所以 OP 的代码 now compiles as is with C++20 没有错误!