I'm playing around with callback functions and wish to register multiple functions via std::bind
that differ in signatures (altough they all return void
). Assigning the result of the std::bind
to the std::variant
yields in a "conversion to non-scalar type" error. Is it an ambiguity error? Can I provide the compiler with more information?
Dropping the std::bind
(which allows the assignment) is not an option as I wish to register the callbacks using some
template <typename Function, typename... Args>
void register(Function &&f, Args &&... args)
{
variant_of_multiple_func_types = std::bind(f, args...);
}
For example:
std::variant<std::function<void()>, int> v = std::bind([]() noexcept {});
works, but
std::variant<std::function<void()>, std::function<void(int)>> v = std::bind([]() noexcept {});
does not, while I expect it to compile into a std::variant
containing a std::function<void()>
.
I get the following compilation error in GCC 7.4.0 with -std=c++17
:
error: conversion from ‘std::_Bind_helper<false, main(int, char**)::<lambda()> >::type {aka std::_Bind<main(int, char**)::<lambda()>()>}’ to non-scalar type ‘std::variant<std::function<void()>, std::function<void(int)> >’ requested
std::variant<std::function<void()>, std::function<void(int)>> v = std::bind([]() noexcept {});
std::bind is a Standard Function Objects that acts as a Functional Adaptor i.e. it takes a function as input and returns a new function Object as an output with with one or more of the arguments of passed function bound or rearranged.
std::bind return type The return type of std::bind holds a member object of type std::decay<F>::type constructed from std::forward<F>(f), and one object per each of args... , of type std::decay<Arg_i>::type, similarly constructed from std::forward<Arg_i>(arg_i).
Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions (via pointers thereto), lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
(since C++17) The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).
std::bind
returns an unspecified object that satisfies certain requirements, but doesn't allow for a distinction between function types based on a signature. The initialization
std::variant<std::function<void()>, std::function<void(int)>> v =
std::bind([]() noexcept {});
is simply ambiguous, same as
std::variant<int, int> v = 42; // Error, don't know which one
You can be explicit about the type you intend to instantiate, e.g.
std::variant<std::function<void()>, std::function<void(int)>> v =
std::function<void()>{std::bind([]() noexcept {})};
This cries for some type aliases, but basically works. A better alternative might be to avoid std::bind
and instead use lambdas, too. Example:
template <typename Function, typename... Args>
void registerFunc(Function &&f, Args &&... args)
{
variant_of_multiple_func_types =
[&](){ std::forward<Function>(f)(std::forward<Args>(args)...); };
}
You can using c++20 std::bind_front
and it will compile:
#include <functional>
#include <variant>
int main()
{
std::variant<std::function<void()>, std::function<void(int)>> v = std::bind_front([]() noexcept {});
std::get<std::function<void()>>(v)();
}
Live demo
According to cppreference:
This function is intended to replace
std::bind
. Unlikestd::bind
, it does not support arbitrary argument rearrangement and has no special treatment for nested bind-expressions orstd::reference_wrapper
s. On the other hand, it pays attention to the value category of the call wrapper object and propagates exception specification of the underlying call operator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With