2

Assume the user defines some subset of the following functions:

void f(int) {}
void g(int) {}
void h(int) {}
// ...

Your task is to write a function call_best(int) which calls the first function from the above list that is declared (you may then assume that it is also defined). How do you do that?

gTcV
  • 2,446
  • 1
  • 15
  • 32
  • 4
    Just curious, what's the use case for this? – Nicolas Holthaus Oct 03 '14 at 09:36
  • All those function are defined. – Paul Evans Oct 03 '14 at 09:55
  • @NicolasHolthaus: I think OP wants to do something like [calling-a-free-function-instead-of-a-method-if-it-doesnt-exist](http://stackoverflow.com/questions/21441803/calling-a-free-function-instead-of-a-method-if-it-doesnt-exist) – Jarod42 Oct 03 '14 at 10:01
  • Yep, that's one possible application. In my case, I use it to let the user implement an optional factory method in case the type he/she provides is not default-constructible (or default construction is not suitable). – gTcV Oct 03 '14 at 10:09
  • 1
    All the functions have identical signature. What does "best" means here? – Emilio Garavaglia Oct 03 '14 at 10:16
  • @EmilioGaravaglia "[...] which calls *the first function* from the above list that is actually defined" So it's up to the library designer to decide what the best function should be. – gTcV Oct 03 '14 at 11:43
  • @gTcV: ok, got it. I was thinking something other, but this way, to me, it's just a +1 to http://stackoverflow.com/a/26176517/924727 – Emilio Garavaglia Oct 03 '14 at 12:19

2 Answers2

4

First, we define a priority class.

template<unsigned P> struct priority : priority<P-1> {};
template<> struct priority<0> {};

It can be used to give a total order on the functions as follows:

template<class Int> auto call_best(Int i, priority<2>) -> decltype(f(i)) { return f(i); }
template<class Int> auto call_best(Int i, priority<1>) -> decltype(g(i)) { return g(i); }
template<class Int> auto call_best(Int i, priority<0>) -> decltype(h(i)) { return h(i); }

void call_best(int i) { call_best(i, priority<2>{}); }

The Int template parameter and the decltype() make sure that only the defined functions compete for being called (keyword SFINAE). The priority tag class allows us to pick the best one among them.

Note that the SFINAE part only works if you have at least one parameter on which you can template. If anyone has an idea on how to avoid this, please tell me.

gTcV
  • 2,446
  • 1
  • 15
  • 32
  • It's enough to just declare one of the functions. Then it'll compile but won't link. I, too, am interested what's the use of this. – jrok Oct 03 '14 at 09:37
  • To the best of my knowledge, it compiles and runs if you define at least one function, see here: http://ideone.com/Jk4Dbo Of course it doesn't compile if you define none of the functions, but that's intended behaviour. At most, one could apply some cosmetic to the error message one gets in that case. – gTcV Oct 03 '14 at 10:05
  • Won't this fail if `f`, `g`, or `h` is overloaded? – D Drmmr Oct 03 '14 at 10:09
  • Why should it? I couldn't produce any problems by overloading `f`. – gTcV Oct 03 '14 at 10:12
  • `decltype()` is an unevaulated context so functions that are declared but not defined can still be "called". – David G Oct 03 '14 at 12:22
  • Suspect there is a misunderstanding on the use of "defined" vs "declared" somewhere. Either the question or the answer shuld be made consistent. If `f` is not "DECLARED" it wil lcompile calling `g`. If `f` is DEFINED it will compile and call `f`. If DECLARED and not DEFINED, will compile to call `f`, but will fail to link. But this last case is not necessarily a bad thing. – Emilio Garavaglia Oct 03 '14 at 12:25
  • You are right, in the question I implicitly assumed that declared and defined are the same thing. I hope I now stated that assumption explicitly enough. Emilio Garavaglia's explanation fully hits the point of what I would like to achieve. – gTcV Oct 03 '14 at 13:37
0

Your code will work if you follow the convention of giving your functions a deduced return type (i.e auto/decltype(auto)). This can only be done in C++14 wherein a function with a deduced return type cannot be used until it is defined (even in an unevaluated operand), causing a substitution failure otherwise. Here's your example that works in clang 3.5 but unfortunately not in g++ 4.9.

template<unsigned P> struct priority : priority<P-1> {};
template<> struct priority<0> {};

// ********
auto f(int);
auto g(int);
auto h(int) { std::cout << "h()"; }
// ********

template<class Int> auto call_best(Int i, priority<2>) -> decltype(f(i)) { return f(i); }
template<class Int> auto call_best(Int i, priority<1>) -> decltype(g(i)) { return g(i); }
template<class Int> auto call_best(Int i, priority<0>) -> decltype(h(i)) { return h(i); }

void call_best(int i) { call_best(i, priority<2>{}); }

int main()
{
    call_best(0); // calls h()
}

clang 3.5 g++ 4.9

David G
  • 94,763
  • 41
  • 167
  • 253