Wrap Overloaded Function via Std::Function

Wrap overloaded function via std::function

That is ambiguous situation.

To disambiguate it, use explicit cast as:

typedef int (*funtype)(const std::string&);

std::function<int(const std::string&)> func=static_cast<funtype>(test);//cast!

Now the compiler would be able to disambiguate the situation, based on the type in the cast.

Or, you can do this:

typedef int (*funtype)(const std::string&);

funtype fun = test; //no cast required now!
std::function<int(const std::string&)> func = fun; //no cast!

So why std::function<int(const std::string&)> does not work the way funtype fun = test works above?

Well the answer is, because std::function can be initialized with any object, as its constructor is templatized which is independent of the template argument you passed to std::function.

std::function fails to distinguish overloaded functions

It's obvious to us which function you intend to be chosen, but the compiler has to follow the rules of C++ not use clever leaps of logic (or even not so clever ones, as in simple cases like this!)

The relevant constructor of std::function is:

template<class F> function(F f);

which is a template that accepts any type.

The C++14 standard does constrain the template (since LWG DR 2132) so that it:

shall not participate in overload resolution unless f is Callable (20.9.12.2) for argument types ArgTypes... and return type R.

which means that the compiler will only allow the constructor to be called when Functor is compatible with the call signature of the std::function (which is void(int, int) in your example). In theory that should mean that void add(A, A) is not a viable argument and so "obviously" you intended to use void add(int, int).

However, the compiler can't test the "f is Callable for argument types ..." constraint until it knows the type of f, which means it needs to have already disambiguated between void add(int, int) and void add(A, A) before it can apply the constraint that would allow it to reject one of those functions!

So there's a chicken and egg situation, which unfortunately means that you need to help the compiler out by specifying exactly which overload of add you want to use, and then the compiler can apply the constraint and (rather redundantly) decide that it is an acceptable argument for the constructor.

It is conceivable that we could change C++ so that in cases like this all the overloaded functions are tested against the constraint (so we don't need to know which one to test before testing it) and if only one is viable then use that one, but that's not how C++ works.

Overload resolution with std::function

In C++11...

Let's take a look at the specification of the constructor template of std::function (which takes any Callable): [func.wrap.func.con]/7-10

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

7 Requires: F shall be CopyConstructible. f shall be Callable (20.10.11.2) for argument types ArgTypes and return type
R. The copy constructor and destructor of A shall not throw
exceptions.

8 Postconditions: !*this if any of the following hold:

  • f is a NULL function pointer.
  • f is a NULL pointer to member.
  • F is an instance of the function class template, and !f

9 Otherwise, *this targets a copy of f initialized with std::move(f). [left out a note here]

10 Throws: shall not throw exceptions when f is a function pointer or a reference_wrapper<T> for some T. Otherwise, may throw
bad_alloc or any exception thrown by F’s copy or move constructor.

Now, constructing, or attempting to construct (for overload resolution) a std::function<void(int)> from a [](){} (i.e. with signature void(void)) violates the requirements of std::function<void(int)>'s constructor.

[res.on.required]/1

Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.

So, AFAIK, even the result of the overload resolution is undefined. Therefore, both versions of g++/libstdc++ are complying in this aspect.


In C++14, this has been changed, see LWG 2132. Now, the converting constructor template of std::function is required to SFINAE-reject incompatible Callables (more about SFINAE in the next chapter):

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

7 Requires: F shall be CopyConstructible.

8 Remarks: These constructors shall not participate in overload
resolution unless f is Callable (20.9.11.2) for argument types
ArgTypes... and return type R.

[...]

The "shall not participate in overload resolution" corresponds to rejection via SFINAE. The net effect is that if you have an overload set of functions foo,

void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);

and a call-expression such as

foo([](std::string){}) // (C)

then the second overload of foo is chosen unambiguously: Since std::function<F> defines F as its interface to the outside, the F defines which argument types are passed into std::function. Then, the wrapped function object has to be called with those arguments (argument types). If a double is passed into std::function, it cannot be passed on to a function taking a std::string, because there's no conversion double -> std::string.
For the first overload of foo, the argument [](std::string){} is therefore not considered Callable for std::function<void(double)>. The constructor template is deactivated, hence there's no viable conversion from [](std::string){} to std::function<void(double)>. This first overload is removed from the overload set for resolving the call (C), leaving only the second overload.

Note that there's been a slight change to the wording above, due to LWG 2420: There's an exception that if the return type R of a std::function<R(ArgTypes...)> is void, then any return type is accepted (and discarded) for the Callable in the constructor template mentioned above. For example, both []() -> void {} and []() -> bool {} are Callable for std::function<void()>. The following situation therefore produces an ambiguity:

void foo(std::function<void()>);
void foo(std::function<bool()>);

foo([]() -> bool {}); // ambiguous

The overload resolution rules don't try to rank among different user-defined conversions, and hence both overloads of foo are viable (first of all) and neither is better.


How can SFINAE help here?

Note when a SFINAE-check fails, the program isn't ill-formed, but the function isn't viable for overload resolution. For example:

#include <type_traits>
#include <iostream>

template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{ std::cout << "foo 1\n"; }

template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{ std::cout << "foo 2\n"; }

int main()
{
foo(42);
foo(42.);
}

Similarly, a conversion can be made non-viable by using SFINAE on the converting constructor:

#include <type_traits>
#include <iostream>

struct foo
{
template<class T, class =
typename std::enable_if< std::is_integral<T>::value >::type >
foo(T)
{ std::cout << "foo(T)\n"; }
};

struct bar
{
template<class T, class =
typename std::enable_if< not std::is_integral<T>::value >::type >
bar(T)
{ std::cout << "bar(T)\n"; }
};

struct kitty
{
kitty(foo) {}
kitty(bar) {}
};

int main()
{
kitty cat(42);
kitty tac(42.);
}

Unresolved overloaded function type in std::transfrom

I don't want to believe that the asker didn't know they are defining two functions with the same name degrees, so I'll give another shade to my answer.

How is it possible, in this call

std::transform(val.begin(), val.end(), std::back_inserter(out), degrees);

that degrees is not known? I mean, std::transform should try to apply degrees to each element in val, and since each of those elements is a double, isn't it obvious that transform should make use of the first overload, the one which takes a double?

As convincing as this motivation might be, though, it would require the compiler to delay/defer the decision of what degrees should be called to the moment it's actually called, i.e. not at the call site of std::transform, but inside std::transform (specifically, when evaluating the expression unary_op(*first1++) in this possible implementation on the cppreference doc page).

This is simply not possible, as the rules are that the compiler must know at the call site of a function what its arguments are. With reference to the example, at the call site of std::transform the compiler has no idea which of the overloads of degree is needed.

One way around is to wrap degrees in a function object with overloaded operator(), as suggested by 463035818_is_not_a_number; doing so, the object degrees would be known at std::transform call site, and only inside std::transform, at the call site of the object's operator() would the compiler have to choose between the overloads of operator().

In C++11, is it possible to wrap a template function in a std::function?

You can't wrap a generic function in a specific instance of std::function, which is what you asked for.

You can, however, wrap a fully-specified instance of a generic function, which is what your code is actually trying to do:

template <typename T>
auto sum(T const& a, T const& b) -> decltype(a+b)
{
return a + b;
}

#include <functional>
#include <iostream>

int main()
{
std::function<int(int,int)> intsum =
std::bind(sum<int>,
std::placeholders::_1, std::placeholders::_2);
auto mysum = intsum(2, 4);

std::cout << mysum << std::endl;
}

Or more simply

  std::function<int(int,int)> intsum = sum<int>;

Or of course if you don't really need to wrap it, just:

  auto mysum = sum(2, 4);

Using std::invoke when a function is overloaded

There are two example solutions:

std::invoke(static_cast<void(S::*)(int)>(&S::foo), s, 1);
std::invoke([&s]() { s.foo(1); });


Related Topics



Leave a reply



Submit