ADL找不到重载功能

时间:2017-07-11 11:48:57

标签: c++ c++11 gcc argument-dependent-lookup

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说:

  

错误:&#39; is_valid&#39;没有在此范围内声明,也没有声明   在实例化时通过参数依赖查找找到   [-fpermissive]

我不确定为什么ADL在bool is_valid(int)实例化之前无法找到S<int> s。我认为这是正确的行为,因为Clang也这么说。所以我试着添加

template<typename T>
bool is_valid(T);

一开始使用函数重载现在Godbolt compiles it fine by Clang or GCC,但不是本地GCC编译或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{});
}

Wandbox

1 个答案:

答案 0 :(得分:2)

解决方案是转发声明is_valid(int)

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

基本类型(如int)的ADL会生成一组空的名称空间和类,因此当您将0传递给S::valid时,您不会引入外部类型is_valid(int)。前向声明有效地让模板知道函数的存在。

关于你在Godbolt中看到的行为......编译器浏览器必须完成一些额外的工作,因为它据称使用的相同gcc和clang版本不适用于任何其他编译器(如Wandbox)

如果确实希望ADL正常工作,那么您需要修改自由函数is_valid,以便ADL是一个选项。我的建议是在与所有自由浮动ADL_Helper函数相同的范围内声明一个辅助结构is_valid,然后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