Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 返回std::variant映射中保存的std::函数_C++_C++17_C++20 - Fatal编程技术网

C++ 返回std::variant映射中保存的std::函数

C++ 返回std::variant映射中保存的std::函数,c++,c++17,c++20,C++,C++17,C++20,我有一个std::variant的映射,其中包含几个std::function专门化,例如: //注意不同的返回类型 使用函数\u t=std::variant; std::map回调; 回调[0]=[](){return 9;}; 如何编写一个调用方(…)帮助函数,该函数将为我的变量在索引处保存的映射std::function提供一个引用,从而允许类似于以下内容的调用: int-value=caller(回调,0)(); 由于函数\u t中包含不同的返回类型,因此简单访问者不起作用,即:

我有一个
std::variant
的映射,其中包含几个
std::function
专门化,例如:

//注意不同的返回类型
使用函数\u t=std::variant;
std::map回调;
回调[0]=[](){return 9;};
如何编写一个
调用方(…)
帮助函数,该函数将为我的变量在索引处保存的映射
std::function
提供一个引用,从而允许类似于以下内容的调用:

int-value=caller(回调,0)();
由于
函数\u t
中包含不同的返回类型,因此简单访问者不起作用,即:

// cannot compile
auto caller(std::map<int, function_t> callbacks, int idx) {
    return std::visit([](const auto& arg) { return arg; }, callbacks[idx]);    
}
//无法编译
自动调用者(std::map回调,intidx){
return std::visit([](const auto&arg){return arg;},回调[idx]);
}

第一部分是仅当参数匹配时才能调用函数:

struct void_t {};

template<class R, class...Args, class...Ts,
  // in C++20 do requires
  std::enable_if_t<sizeof...(Args)==sizeof...(Ts), bool> = true,
  class R0=std::conditional_t< std::is_same_v<R,void>, void_t, R >
>
std::optional<R0> call_me_maybe( std::function<R(Args...)> const& f, Ts&&...ts ) {

  if constexpr ( (std::is_convertible_v<Ts&&, Args> && ... ))
  {
    if constexpr (std::is_same_v<R, void>) {
      f(std::forward<Ts>(ts)...);
      return void_t{};
    } else {
      return f(std::forward<Ts>(ts)...);
    }
  }
  else
  {
    return std::nullopt;
  }
}
template<class R, class...Args, class...Ts,
  // in C++20 do requires
  std::enable_if_t<sizeof...(Args)!=sizeof...(Ts), bool> = true,
  class R0=std::conditional_t< std::is_same_v<R,void>, void_t, R >
>
constexpr std::optional<R0> call_me_maybe( std::function<R(Args...)> const& f, Ts&&...ts ) {
  return std::nullopt;
}
将会有一些编译器不喜欢我使用的
autoi
;关于这些,<代码> DeCype(I)::值<代码>替换<代码> i>代码>可能会帮助(我可以说,不是所有编译器都是C++兼容的)。 基本思想是,我们创建一个变量,其中包含函数可能返回值的匹配索引。然后,我们返回其中的一个可选选项,以处理(在运行时)失败肯定是可能的这一事实

call\u me\u也许
是(除了歌曲参考之外)一种假装我们可以调用任何东西的方式。这就是当
R
void
时,
nothing\u t
可能有用的地方

variant\u index\t
是一个技巧,我使用它将变量作为泛型和类型处理,其中可能包含重复类型

首先,我们定义一个编译时整数,称为
索引
。它基于现有的
std::integral_常量

然后我们做了一个变体,这样备选方案3就是编译时索引3

然后,我们可以使用
std::visit([&](auto I){/*…*/},get_variant_index(var))
将变量的索引作为编译时常量使用

如果
var
有4个备选方案并保留备选方案2,则
get\u variant\u index
返回一个
std::variant
,其中填充了
index

(在运行时,这似乎是由一个64位整数
2
表示的。我觉得这很有趣。)

当我们
std::visit
this
variant\u index
时,我们传递的lambda将传递
index\u t
。因此lambda有一个编译时常量传递给它。在非哑编译器中,您可以通过
操作符std::size\t
隐式地从
索引中提取值。对于哑编译器,必须执行
std::decay\u t::value
,这将是相同的编译时整数

使用编译时整数,我们可以
std::get(var)
lambda内的值(并保证位于正确位置的值),并且我们可以使用它在同一备选方案中构造另一个变量,即使另一个变量有不明确的备选方案。对你来说,如果你有

std::function<int(int)>
std::function<int(int,int)>
我得到这个输出:

前两行是记录调用的
void()
int(int)
std::function
s

第三行显示了哪些调用成功——对
void()
的0参数调用和对
int(int)
的1参数调用

最后4行是存储的结果。第一个是
可选
,它处于接合状态并保持
无效状态。第二次和第三次调用失败,因此
nullopt
,最后一次调用包含将
1
传递给返回
1+1
的函数的结果

从返回值中,您可以查看调用是否有效(查看外部可选项是否已启用),确定调用了哪个回调(如果调用了),并获取被调用变量的值(对其进行访问)


如果函数类型的数量很大,则需要考虑优化。

上面有两个嵌套的变量索引
std::visions
,保证返回相同的值。这意味着在只需要O(n)的情况下生成O(n^2)代码,其中n是
功能\u t
中的备选方案数

您可以通过将变量索引“down”作为额外参数传递给
call\u maybe
var\u opt\u flip
来清除这个问题。理论上,编译器可以计算出其他n^2-n生成的代码元素是不可访问的,但这两个元素都需要编译器进行大量工作,即使它工作起来也很脆弱

这样做将减少构建时间(这种愚蠢的行为可能会花费构建时间;不要在通常包含的公共头中调用它!),并且可以减少运行时可执行文件的大小


大多数编程语言和C++的大多数用法不允许O(n)代码生成多于O(n)的二进制;但模板功能足够强大,特别是std变体,可以生成O(n^2)甚至O(n^3)二进制代码输出。因此,应该注意一些。

第一部分是仅当参数匹配时才能调用函数:

struct void_t {};

template<class R, class...Args, class...Ts,
  // in C++20 do requires
  std::enable_if_t<sizeof...(Args)==sizeof...(Ts), bool> = true,
  class R0=std::conditional_t< std::is_same_v<R,void>, void_t, R >
>
std::optional<R0> call_me_maybe( std::function<R(Args...)> const& f, Ts&&...ts ) {

  if constexpr ( (std::is_convertible_v<Ts&&, Args> && ... ))
  {
    if constexpr (std::is_same_v<R, void>) {
      f(std::forward<Ts>(ts)...);
      return void_t{};
    } else {
      return f(std::forward<Ts>(ts)...);
    }
  }
  else
  {
    return std::nullopt;
  }
}
template<class R, class...Args, class...Ts,
  // in C++20 do requires
  std::enable_if_t<sizeof...(Args)!=sizeof...(Ts), bool> = true,
  class R0=std::conditional_t< std::is_same_v<R,void>, void_t, R >
>
constexpr std::optional<R0> call_me_maybe( std::function<R(Args...)> const& f, Ts&&...ts ) {
  return std::nullopt;
}
将会有一些编译器不喜欢我使用的
autoi
;关于这些,<代码> DeCype(I)::值<代码>替换<代码> i>代码>可能会帮助(我可以说,不是所有编译器都是C++兼容的)。 基本思想是,我们创建一个变量,其中包含函数可能返回值的匹配索引。然后,我们返回其中的一个可选选项,以处理(在运行时)失败肯定是可能的这一事实

call\u me\u也许
是(除了歌曲参考之外)一种假装我们可以调用任何东西的方式。这就是当使用
R
时,
nothing\u t
可能有用的地方
template<class...Sigs, class...Ts>
auto call_maybe( std::variant<std::function<Sigs>...> const& vf, Ts&&...ts )
{
  using R0 = std::variant< decltype(call_me_maybe(std::function<Sigs>{}, std::forward<Ts>(ts)...))... >;
  R0 retval = std::visit(
    [&](auto I)->R0 {
      return R0( std::in_place_index_t<I>{}, call_me_maybe(std::get<I>(vf), std::forward<Ts>(ts)... ) );
    },
    get_variant_index(vf)
  );
  return var_opt_flip( std::move(retval) );
}
using function_t = std::variant< std::function< void() >, std::function< int(int) > >;

template<class...Ts>
auto caller(std::map<int, function_t> const& callbacks, int idx, Ts&&...ts) {
  auto it = callbacks.find(idx);
  using R = decltype(call_maybe( it->second, std::forward<Ts>(ts)... ));
  // wrong index:
  if (it == callbacks.end())
    return R(std::nullopt);
  // ok, give it a try:
  return call_maybe( it->second, std::forward<Ts>(ts)... );
}
std::function<int(int)>
std::function<int(int,int)>
    std::map<int, function_t> callbacks = {
        { 0, []{ std::cout << 0 << "\n"; } },
        { 1, [](int x){ std::cout << "1:" << x << "\n"; return x+1; } },
    };
    std::optional<std::variant<void_t, int>> results[] = {
        caller(callbacks, 0),
        caller(callbacks, 0, 1),
        caller(callbacks, 1),
        caller(callbacks, 1, 1),
    };
    for (auto&& op:results) {
        std::cout << (bool)op;
    }
    std::cout << "\n";
    auto printer = [](auto val) {
        if constexpr (std::is_same_v<decltype(val), void_t>) {
            std::cout << "void_t";
        } else {
            std::cout << val;
        }
    };
    int count = 0;
    for (auto&& op:results) {
        
        std::cout << count << ":";
        if (!op) {
            std::cout << "nullopt\n";
        } else {
            std::visit( printer, *op );
            std::cout << "\n";
        }
        ++count;
    }
0
1:1
1001
0:void_t
1:nullopt
2:nullopt
3:2