Reputation: 2504
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?
Upvotes: 1
Views: 198
Reputation: 217135
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{});
}
Upvotes: 3
Reputation: 137310
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
Upvotes: 4