Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function overloading - order of definitions

Tags:

c++

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:

  • I want it to be resolved at compile-time.
  • It should be as simple as possible in user code. For example, having 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.

like image 880
CygnusX1 Avatar asked Dec 12 '18 17:12

CygnusX1


People also ask

Does order of arguments matter in function overloading?

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.

What are the rules for defining overloaded function?

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.


1 Answers

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.

like image 55
Max Langhof Avatar answered Oct 11 '22 23:10

Max Langhof