I have a situation where I need overload resolution to prefer an overload with an implicit conversion over a template function with the same name.
Consider the following example:
#include <iostream>
#include <functional>
void call_function(const std::function<void()>& function)
{
std::cout << "CALL FUNCTION 1" << std::endl;
function();
}
template <typename Function>
void call_function(const Function& function)
{
std::cout << "CALL FUNCTION 2" << std::endl;
function();
}
int main()
{
// Pass a lambda to "call_function"
// This lambda is implicitly convertible to 'std::function<void()>'
// Even though it is implicitly convertible, the template function is selected by the compiler.
call_function([]{
std::cout << "TEST" << std::endl;
});
}
Output:
CALL FUNCTION 2
TEST
Unfortunately, the compiler seems to detect that the first implementation of call_function
would require an implicit conversion to convert the lambda I pass it into a std::function<void()>
object and because of this it determines that the template version is a better match and uses the template. I need to force the compiler to prefer the implicit conversion overload over the template so the output would be:
CALL FUNCTION 1
TEST
How can I achieve this? (Also note that I am restricted to a C++11 compliant compiler so I am unable to use features from C++14 and beyond)
If you want to change the overloads such that the former overload is chosen whenever there is an implicit conversion possible, with the latter being the backup, you can do this with SFINAE via std::enable_if
:
#include <type_traits>
void call_function(const std::function<void()>& function)
{
std::cout << "CALL FUNCTION 1" << std::endl;
function();
}
template <typename Function,
// Consider this overload only if...
typename std::enable_if<
// the type cannot be converted to a std::function<void()>
!std::is_convertible<const Function&, std::function<void()>>::value,
int>::type = 0>
void call_function(const Function& function)
{
std::cout << "CALL FUNCTION 2" << std::endl;
function();
}
Alternatively, if you want to be able to support an unknown number of overloads of call_function
with the "CALL FUNCTION 2"
being a backup overload in case none of the functions work, you can do this too, but it requires quite a bit more work:
// Rename the functions to `call_function_impl`
void call_function_impl(const std::function<void()>& function)
{
std::cout << "CALL FUNCTION 1" << std::endl;
function();
}
void call_function_impl(const std::function<void(int, int)>& function)
{
std::cout << "CALL FUNCTION 2" << std::endl;
function(1, 2);
}
// The backup function must have a distinct name
template <typename Function>
void call_function_backup_impl(const Function& function)
{
std::cout << "CALL FUNCTION backup" << std::endl;
function();
}
// Implement std::void_t from C++17
template <typename>
struct void_impl {
using type = void;
};
template <typename T>
using void_t = typename void_impl<T>::type;
// Make a type trait to detect if the call_function_impl(...) call works
template <typename Function, typename = void>
struct has_call_function_impl
: std::false_type
{};
template <typename Function>
struct has_call_function_impl<Function,
void_t<decltype(call_function_impl(std::declval<const Function&>()))>>
: std::true_type
{};
// If the call_function_impl(...) call works, use it
template <typename Function,
typename std::enable_if<
has_call_function_impl<Function>::value,
int>::type = 0>
void call_function(const Function& function)
{
call_function_impl(function);
}
// Otherwise, fall back to the backup implementation
template <typename Function,
typename std::enable_if<
!has_call_function_impl<Function>::value,
int>::type = 0>
void call_function(const Function& function)
{
call_function_backup_impl(function);
}