调用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
为什么?
答案 0 :(得分:22)
这是因为您尝试显式实例化get
函数模板,方法是提供0
作为模板参数。对于模板,如果在调用点处显示具有该名称的函数模板,则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<0>(s)
时会触发ADL。
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 }
-end example] -end note]
答案 1 :(得分:13)
ADL不直接应用于 template-id ,例如get<0>
,因此编译器并没有真正开始沿着这条路径开始。 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
请注意,上面的not_used_for_anything
仅仅是一种安全机制。它的目的是一个永不完整的不完整类型。省略它也可以,但是不安全,因为它可能会与您可能想要的签名冲突。
template< typename >
void get() = delete;
注意:标准的上述引用是非规范性的,这意味着委员会认为,我们可以在没有解释的情况下解决这个问题,因为它隐含在语言和语法的其余部分,特别是事实上,3.4.2没有提到查找模板ID。是的,没错!