Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::bind to a std::variant containing multiple std::function types

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 {});
like image 793
Tmplt Avatar asked Aug 29 '19 11:08

Tmplt


People also ask

What does std :: bind do in C++?

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.

What is the return type of std :: bind?

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).

Is std :: function copyable?

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.

What is std :: variant in C++?

(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).


2 Answers

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)...); };
}
like image 149
lubgr Avatar answered Sep 21 '22 06:09

lubgr


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. Unlike std::bind, it does not support arbitrary argument rearrangement and has no special treatment for nested bind-expressions or std::reference_wrappers. 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.

like image 22
康桓瑋 Avatar answered Sep 22 '22 06:09

康桓瑋