Resolve overload ambiguity with SFINAE

2019-05-25 03:56发布

问题:

I've found similar cases, but they usually ended up doing something along the lines of what I (think) I'm doing here.

I want to be able to call a function with one or more parameters, obviously, if the function exists with overloads with multiple parameters, the correct version cannot be deduced without help.

As I am specifying the number of arguments as well, I figured this would be enough information for the compiler to deduce the correct overload. This doesn't seem to be the case and I hope you may be able to show me why.

the code: http://coliru.stacked-crooked.com/a/5e6fd8d5418eee3c

#include <iostream>
#include <type_traits>
#include <functional>

template < typename R, typename... A, typename... Args >
typename std::enable_if< sizeof...( A ) == sizeof...( Args ), R >::type
call_my_function( R(*func)(A...), Args ...a )
{
    return func( a... );
}

int arg_count() { return 0; }
int arg_count(int) { return 1; }
int arg_count(int,int) { return 2; }

int main()
{
    std::cout << call_my_function( arg_count, 0 ) << std::endl;
    return 0;
}

In short, I tried to have all functions which have a different argument count than the number of arguments I supplied, fail by means of SFINAE. But it seems they are considered anyway and the ambiguity remains.

回答1:

Unfortunately not; SFINAE can be used for selecting between different function template definitions, but not between function overloads passed as an argument.

This is because an overloaded function passed as an argument must be resolved to a single overload before dependent types in the template function definition are evaluated and SFINAE kicks in.

You can see this by creating n overloaded template definitions, where n - 1 is the maximum number of arguments you want to handle:

template < typename R, typename... Args >
typename std::enable_if< 0 == sizeof...( Args ), R >::type
call_my_function( R(*func)(), Args ...a )
{
    return func( a... );
}

template < typename R, typename A1, typename... Args >
typename std::enable_if< 1 == sizeof...( Args ), R >::type
call_my_function( R(*func)(A1), Args ...a )
{
    return func( a... );
}

template < typename R, typename A1, typename A2, typename... Args >
typename std::enable_if< 2 == sizeof...( Args ), R >::type
call_my_function( R(*func)(A1, A2), Args ...a )
{
    return func( a... );
}

Here each arg_count resolves to exactly one call_my_function definition, so there is no ambiguity within a particular call_my_function definition as to which arg_count was passed.

A possible solution would be to generate these n overloads either manually or using the preprocessor (e.g. using Boost.Preprocessor).