Reputation: 16266
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?
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{});
}
Upvotes: 1
Views: 468
Reputation: 41100
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);
}
Upvotes: 2