std :: bind到包含多种std :: function类型的std :: variant

Tmp*_*plt 10 c++ std-function c++17 std-variant

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...);
}
Run Code Online (Sandbox Code Playgroud)

For example:

std::variant<std::function<void()>, int> v = std::bind([]() noexcept {});
Run Code Online (Sandbox Code Playgroud)

works, but

std::variant<std::function<void()>, std::function<void(int)>> v = std::bind([]() noexcept {});
Run Code Online (Sandbox Code Playgroud)

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 {});
Run Code Online (Sandbox Code Playgroud)

lub*_*bgr 9

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 {});
Run Code Online (Sandbox Code Playgroud)

is simply ambiguous, same as

std::variant<int, int> v = 42; // Error, don't know which one
Run Code Online (Sandbox Code Playgroud)

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 {})};
Run Code Online (Sandbox Code Playgroud)

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)...); };
}
Run Code Online (Sandbox Code Playgroud)


康桓瑋*_*康桓瑋 7

您可以使用c ++ 20 std::bind_front,它将编译:

#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)();
}
Run Code Online (Sandbox Code Playgroud)

现场演示

根据cppreference

此功能旨在替代std::bind。不同于std::bind,它不支持任意参数重排,并且对嵌套的bind-expressions或std::reference_wrappers 没有特殊的处理。另一方面,它注意调用包装对象的值类别,并传播基础调用操作程序的异常规范。