I am trying to create a library which has some complex resolve
function, that calls a simple foo
function. The resolve
function should work for any type for which foo
is overloaded.
The foo
provided in the library works with some basic types, but I want the user to be able to provide its own, overloaded versions of foo
that could be used in resolve
.
Here is a very short example:
//IN THE LIBRARY
#include <iostream>
void foo(int i) { std::cout << "int " << i; }
void foo(float f) { std::cout << "float " << f; }
template <typename T>
void resolve(T arg) {
//some complex computation
foo(arg);
//some more complutation
}
//IN THE USER CODE
void foo(const char* c) { std::cout << "cstring " << c; }
int main() {
resolve(3);
resolve(4.2f);
resolve("abc");
return 0;
}
The problem is, that the user-defined overload for foo
appears after the definition of resolve
and it is not seen. I was expecting that at the time of instantiation resolve<const char*>
the 3-rd overload of foo
would be seen – but that is seems not to be the case:
prog.cpp: In instantiation of ‘void resolve(T) [with T = const char*]’:
prog.cpp:16:15: required from here
prog.cpp:8:5: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
foo(arg);
~~~^~~~~
prog.cpp:3:6: note: initializing argument 1 of ‘void foo(int)’
void foo(int i) { std::cout << "int " << i; }
^~~
So, my question is, how can I make it work? Is there some easy way of having parts of resolve
to be user-provided (e.g. in terms of foo
overloading)?
Some constraints:
resolve
take an additional lambda parameter – which should always be the same for a given type of arg
– is too complex in use.The use scenario is similar to overloading the std::less
for given custom T
, and then using std::sort
without taking any additional arguments, besides the array of T
that should be sorted.
In method overloading, the class can have multiple methods with the same name but the parameter list of the methods should not be the same. One way to make sure that the parameter list is different is to change the order of the arguments in the methods.
To overload functions, they must have a different= set of parameters, i.e., either have parameters of different data types or a different number of parameters in the function definition. Functions with different return types and identical parameters can not be overloaded.
The rules regarding name lookups in templates are detailed here:
For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations [...] that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations [...] that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL).
There are thus two options for you:
Users can only invoke your resolve
with arguments that either have the specialization provided by you (by default) or that can be found via Argument Dependent Lookup. Basically, they must define their overloads in the same namespace as the types they pass.
Example: https://godbolt.org/z/KCaO-s
You make foo
a template which users can specialize. You provide your own (default) specializations, the user can later provide theirs and resolve
will choose the best template specialization available at the point of instantiation for resolve
(i.e. where resolve
is called).
Example: https://godbolt.org/z/AYn7FM
Addendum:
The standard library has a very similar problem; see e.g. here for how customizing e.g. std::swap
behavior by users is achieved (as in, allowed). Note in particular that with C++20, function template specializations are no longer allowed (only class template specializations). The reasons for that are detailed in the corresponding paper. While the above advice is more comfortable for users than requiring class specializations everywhere, you should probably look into the reasons why the standard library decided to take a more extreme stance.
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