ADL 找不到重载函数
ADL cannot find overloaded function
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a); }
};
bool is_valid(int)
{ return true; }
int main()
{
S<int> s;
s.valid(0);
}
VS 编译这个示例很好,而 GCC 说:
error: 'is_valid' was not declared in this scope, and no declarations
were found by argument-dependent lookup at the point of instantiation
[-fpermissive]
我不确定为什么 ADL 找不到在 S<int> s
实例化之前定义的 bool is_valid(int)
。我想这是正确的行为,正如 Clang 所说的那样。所以我尝试添加
template<typename T>
bool is_valid(T);
一开始使用函数重载现在Godbolt compiles it fine by Clang or GCC, but not local GCC compilation or on Ideone.
在这种情况下,如何使用 ADL 在模板声明 (GCC) 之后提供函数定义?奖励:为什么 Godbolt 编译最后一个样本?
解决方案:
感谢接受的答案,我发现问题在于 ADL 专门处理基本类型。这帮助我最终得到了以下解决方案,该解决方案使用模板函数的前向声明,可以为用户定义的类型重载或专门用于原始类型。
template<typename T>
bool is_valid(T);
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a); }
};
template<>
bool is_valid<int>(int)
{ return true; }
struct User_data
{};
bool is_valid(User_data)
{ return true; }
int main()
{
S<int> s_primitive;
s_primitive.valid(0);
S<User_data> s_user_data;
s_user_data.valid(User_data{});
}
解决方法是转发声明is_valid(int)
:
bool is_valid(int);
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a); }
};
基本类型的 ADL(如 int
)会生成一组空的命名空间和 类 以供考虑,因此当您将 0
传递给 S::valid
时,您不引入外部 is_valid(int)
。前向声明有效地让模板知道函数存在。
关于您在 Godbolt 中看到的行为...编译器资源管理器必须完成一些额外的工作,因为据称它使用的相同 gcc 和 clang 版本不适用于任何其他编译器(如 Wandbox)
如果您真的想要 ADL 工作,那么您需要修改自由函数 is_valid
,使 ADL 成为一个选项。我的建议是在与所有自由浮动 is_valid
函数相同的范围内声明一个辅助结构 ADL_Helper
,然后 S::is_valid
将传递一个实例:
struct ADL_Helper{};
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a, ADL_Helper{}); }
};
bool is_valid(int, ADL_Helper)
{ return true; }
int main()
{
S<int> s;
s.valid(0);
}
Demo
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a); }
};
bool is_valid(int)
{ return true; }
int main()
{
S<int> s;
s.valid(0);
}
VS 编译这个示例很好,而 GCC 说:
error: 'is_valid' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
我不确定为什么 ADL 找不到在 S<int> s
实例化之前定义的 bool is_valid(int)
。我想这是正确的行为,正如 Clang 所说的那样。所以我尝试添加
template<typename T>
bool is_valid(T);
一开始使用函数重载现在Godbolt compiles it fine by Clang or GCC, but not local GCC compilation or on Ideone.
在这种情况下,如何使用 ADL 在模板声明 (GCC) 之后提供函数定义?奖励:为什么 Godbolt 编译最后一个样本?
解决方案:
感谢接受的答案,我发现问题在于 ADL 专门处理基本类型。这帮助我最终得到了以下解决方案,该解决方案使用模板函数的前向声明,可以为用户定义的类型重载或专门用于原始类型。
template<typename T>
bool is_valid(T);
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a); }
};
template<>
bool is_valid<int>(int)
{ return true; }
struct User_data
{};
bool is_valid(User_data)
{ return true; }
int main()
{
S<int> s_primitive;
s_primitive.valid(0);
S<User_data> s_user_data;
s_user_data.valid(User_data{});
}
解决方法是转发声明is_valid(int)
:
bool is_valid(int);
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a); }
};
基本类型的 ADL(如 int
)会生成一组空的命名空间和 类 以供考虑,因此当您将 0
传递给 S::valid
时,您不引入外部 is_valid(int)
。前向声明有效地让模板知道函数存在。
关于您在 Godbolt 中看到的行为...编译器资源管理器必须完成一些额外的工作,因为据称它使用的相同 gcc 和 clang 版本不适用于任何其他编译器(如 Wandbox)
如果您真的想要 ADL 工作,那么您需要修改自由函数 is_valid
,使 ADL 成为一个选项。我的建议是在与所有自由浮动 is_valid
函数相同的范围内声明一个辅助结构 ADL_Helper
,然后 S::is_valid
将传递一个实例:
struct ADL_Helper{};
template<typename T>
struct S
{
bool valid(T a)
{ return is_valid(a, ADL_Helper{}); }
};
bool is_valid(int, ADL_Helper)
{ return true; }
int main()
{
S<int> s;
s.valid(0);
}