为何针对依赖型别时ADL会失效?
为什么模板中依赖模板参数的tuple无法通过ADL匹配std::get?
先看问题代码:
template <typename T> void foo(T) { std::tuple<T> tup{0}; get<0>(tup) = 1; // 报错:'get' was not declared in this scope; did you mean 'std::get'? }
这个问题的核心在于模板的两阶段查找规则和带显式模板参数的函数调用的特殊性:
模板编译分两个阶段:
- 模板定义阶段:编译器会查找所有非依赖名字(不依赖模板参数的名字),同时做基础语法检查。
- 模板实例化阶段:才会处理依赖于模板参数的名字,包括触发ADL(实参依赖查找)。
当你写
get<0>(tup)时,get后跟了显式模板参数<0>,编译器会把它当成带显式模板参数的函数调用。这种情况下,编译器必须在模板定义阶段就确定get是一个函数模板——否则它会误以为<是小于运算符,直接触发语法错误。但在模板定义阶段,
tup的类型是std::tuple<T>,T是模板参数,属于依赖类型。此时编译器还无法确定tup的具体命名空间(虽然我们知道是std,但编译器在定义阶段不会提前推导),而ADL要等到实例化阶段才会触发,导致定义阶段找不到get对应的函数模板,最终报错。
再看两种能正常运行的情况:
- 非模板函数中,
tup是std::tuple<int>,属于已知类型,编译器在编译时直接触发ADL,去std命名空间找到std::get模板,匹配成功。 - 添加
using std::get;后,std::get被引入当前作用域,模板定义阶段就能找到get是函数模板,实例化时直接匹配调用即可。
总结:带显式模板参数的函数调用,要求编译器在模板定义阶段就找到对应的函数模板,而ADL是实例化阶段才执行的查找逻辑,所以这里ADL无法生效,必须提前把std::get引入作用域,或者直接显式指定std::前缀。
内容的提问来源于stack exchange,提问作者bitmask
相关产品推荐
相关产品推荐

