1

Assume the following two functions may or may not be provided by the user:

void foo(int) { std::cout << "foo int" << std::endl; }
void foo() { std::cout << "foo void" << std::endl; }

In my implementation, I want to call foo(int) if the user defined it, and foo() otherwise. This can be done as follows:

template<class Int> auto call_foo(Int i) -> decltype(foo(i)) { return foo(i); }
template<class... Int> void call_foo(Int... i) { return foo(); } 

If, however, we want to do the opposite, i.e. prefer foo() over foo(int), the following naive attempt does not work.

template<class Int> auto call_foo(Int i) -> decltype(foo()) { return foo(); }
template<class... Int> void call_foo(Int... i) { return foo(i...); } 

The problem is that decltype(foo()) does not depend on Int, so the possible non-existence of foo() does not result in SFINAE.

A possible solution is to require the user to define either of

void foo(int, void*) { std::cout << "foo int" << std::endl; }
void foo(void*) { std::cout << "foo void" << std::endl; }

Like this, we always have a parameter of foo on which we can do the "template vs. parameter-pack" trick. While this technically solves the problem, it's quite ugly as it requires the user to take an additional parameter whose meaning might not be obvious to him/her. So is there a way to achieve the same effect without the additional parameter?

gTcV
  • 2,446
  • 1
  • 15
  • 32

2 Answers2

4

No need for SFINAE. Just standard overload resolution is sufficient.

void call_func(void (*f)(), int) { f(); } // #1

template<class = void>
void call_func(void (*f)(int), int x) { f(x); } // #2

call_func(foo, 1); // #1 is preferred if viable. All others being equal,
                   // non-templates are preferred over templates
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • +1. Although I was hoping to see a nice TMP solution which would work for a user specified precedence. For example, if he has `N` `foo`'s which all take the corresponding number of `int`s, and the user wants to specify some permutation of `{1,...,N}` as the precedence order. Of course, I don't see any practical significance. Just that it would lead to some interesting code :) – Pradhan Oct 03 '14 at 08:17
  • 1
    You may use *tag inheritance* to order multiple overload (and avoid template). – Jarod42 Oct 03 '14 at 08:23
  • Your solution is perfect for the situation I described in the question. Unfortunately, in the actual code I have more than two functions and not all of them have the same name. Then, you do need SFINAE to rule out the non-existing names. – gTcV Oct 03 '14 at 09:00
  • @Pradhan You find something in that direction here: http://stackoverflow.com/questions/26176516/choose-best-available-function-through-tag-inheritance – gTcV Oct 03 '14 at 09:32
3

Following may help:

template<typename ... Ts> void foo(Ts...) = delete;
void foo(int) { std::cout << "foo int" << std::endl; }
//void foo() { std::cout << "foo void" << std::endl; }

namespace detail
{

struct overload_priority_low {};
struct overload_priority_high : overload_priority_low{};

template <typename Int>
auto call_foo(Int i, overload_priority_low) -> decltype(foo(i)) { return foo(i); }

template <typename Int>
auto call_foo(Int , overload_priority_high) -> decltype(sizeof(Int), foo()) { return foo(); }

}

void call_foo(int i)
{
    return detail::call_foo(i, detail::overload_priority_high{});
}

Live example.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Very useful trick, this tag inheritance. I cleaned your code up here: http://stackoverflow.com/questions/26176516/choose-best-available-function-through-tag-inheritance – gTcV Oct 03 '14 at 09:31
  • @MarcoA.: Removing the `=delete` seems to fix the error with clang. – Jarod42 Oct 03 '14 at 09:44