简体   繁体   中英

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:

error: 'is_valid' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]

I'm not sure why ADL cannot find bool is_valid(int) as it's defined before S<int> s instantiation. I suppose it's correct behaviour as Clang says the same. 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 .

How can I use ADL in this case to provide function definition after template declaration (GCC)? Bonus: why Godbolt compiles the last sample?

Solution:

Thanks to the accepted answer, I figured out that the problem is that ADL treats primitive types specifically. 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

The solution is to forward declare 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) . 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)


If you really want ADL to work, then you need to modify the free function is_valid such that ADL is an option. 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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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