简体   繁体   English

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 compiles this sample fine while GCC says: 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] 错误:在此范围内未声明'is_valid',并且在实例化点[-fpermissive]依赖于参数的查找未找到任何声明

I'm not sure why ADL cannot find bool is_valid(int) as it's defined before S<int> s instantiation. 我不确定为什么ADL找不到在S<int> s实例化之前定义的bool is_valid(int) I suppose it's correct behaviour as Clang says the same. 我想这是正确的行为,就像Clang所说的一样。 So I tried to add 所以我尝试添加

template<typename T>
bool is_valid(T);

at the beginning to use function overloading and now Godbolt compiles it fine by Clang or GCC , but not local GCC compilation or on Ideone . 刚开始使用函数重载,现在Godbolt通过Clang或GCC对其进行了很好的编译 ,但对本地GCC编译或在Ideone上没有进行过编译。

How can I use ADL in this case to provide function definition after template declaration (GCC)? 在这种情况下,如何在模板声明(GCC)之后使用ADL提供函数定义? Bonus: why Godbolt compiles the last sample? 奖励:为什么Godbolt会编译最后一个样本?

Solution: 解:

Thanks to the accepted answer, I figured out that the problem is that ADL treats primitive types specifically. 多亏了已接受的答案,我才发现问题在于ADL特别对待原始类型。 This helped me to end up with following solution that uses forward declaration of templated function which can be overloaded for user-defined types or specialised for primitive types. 这帮助我最终得到了以下解决方案,该解决方案使用模板化函数的前向声明,该函数对于用户定义的类型而言可以重载,或者对于原始类型可以专门化。

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{});
}

Wandbox Wandbox

The solution is to forward declare is_valid(int) : 解决方案是转发声明is_valid(int)

bool is_valid(int);
template<typename T>
struct S
{
    bool valid(T a)
    { return is_valid(a); }
};

ADL for fundamental types (like int ) produces an empty set of namespaces and classes to consider, so when you pass 0 into S::valid , you're not bringing in the outer is_valid(int) . 基本类型(如int )的ADL会生成一组空的名称空间和要考虑的类,因此,当您将0传递给S::valid ,就不会引入外部的is_valid(int) Forward declaration effectively lets the template know the function exists. 前向声明有效地使模板知道该函数存在。

Regarding the behavior you see in Godbolt... there must be some extra work being done by the compiler explorer because the same gcc and clang versions it is allegedly using do not work on any other compiler (like Wandbox) 关于您在Godbolt中看到的行为...编译器资源管理器必须完成一些额外的工作,因为据称所使用的相同gcc和clang版本不适用于任何其他编译器(例如Wandbox)


If you really want ADL to work, then you need to modify the free function is_valid such that ADL is an option. 如果您确实希望ADL工作,则需要修改自由函数is_valid ,以便可以选择ADL。 My recommendation is to declare a helper struct ADL_Helper in the same scope as all your free-floating is_valid functions, then S::is_valid will pass an instance: 我的建议是在与所有自由浮动的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 演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM