Is there some way to force C++ compilers to perform name lookup for a given symbol during template instantiation (and not before)?
Given the following code:
template <class T>
auto wrapper( T t ) -> decltype( f( t ) )
{
return f( t );
}
unsigned char f( int x ) { return x % 256; }
unsigned char f( unsigned char x ) { return x; }
int main( int, char ** )
{
auto x = wrapper( 3100 );
return 0;
}
Is there anything I can do (apart from moving the definition of f
to the top) in order to make that code compile and give the same results as if all definitions of f
were available before the definition of wrapper
?
I could not find anything, probably because I do not know how to phrase this question properly. All argument types of f
can be assumed to be user-defined types, if this helps.
Is there some way to force C++ compilers to perform name lookup for a given symbol during template instantiation (and not before)?
Yes. First of all, the name must be dependent. The name f
in wrapper
when used as f(t)
is dependent because t
is type-dependent. [temp.dep]/1:
In an expression of the form:
postfix-expression
(
expression-list opt)
where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if
- any of the expressions in the expression-list is a pack expansion (14.5.3),
- any of the expressions in the expression-list is a type-dependent expression (14.6.2.2), or
- if the unqualified-id is a template-id in which any of the template arguments depends on a template parameter.
The problem is that names declared after the template itself, i.e. only in the instantiation but not the definition context, can solely be found using argument dependent name lookup. Your f
overloads only take fundamental types, but those do not have the global namespace associated with them according to [basic.lookup.argdep]/2:
If
T
is a fundamental type, its associated sets of namespaces and classes are both empty.
Thus the f
s you declared can never be found if the arguments are of the same type as the parameters. A little trick can help:
template <typename T>
struct refwrap
{
T&& t;
refwrap(T&& t) : t(std::forward<T>(t)) {}
operator T&&() {return std::forward<T>(t);}
};
template <typename T>
auto make_refwrap( T&& t ) -> refwrap<T> // making use of reference collapsing
{ return {std::forward<T>(t)}; } // inside refwrap to get forwarding
This template, when declared in the global namespace, will cause ADL to consider it. Rewrite wrapper
as follows:
template <class T>
auto wrapper( T t ) -> decltype( f( make_refwrap(t) ) )
{
return f( make_refwrap(t) );
}
Demo. This is not the proper way to do it though, as it will fail in more complex scenarios.
This would work with template specialization. Note that you have to decide what the default function is, because i cannot see it in question.
// default function
template <class T>
unsigned char f( T x ) { return x; }
// specialization for int
template <>
unsigned char f( int x ) { return x % 256; }
int main( int, char ** )
{
auto x = f( 3100 );
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With