【问题标题】:Cannot initialize std::variant with various lambda expressions无法使用各种 lambda 表达式初始化 std::variant
【发布时间】:2018-01-14 00:47:48
【问题描述】:

我正在玩std::variant, lambdasstd::future,当我尝试将它们组合在一起时得到了非常奇怪的结果。以下是示例:

using variant_t = std::variant<
    std::function<std::future<void>(int)>,
    std::function<void(int)>
>;
auto f1 = [](int) { return std::async([] { return 1; }); };
auto f2 = [](int) { return std::async([] {  }); };

variant_t v1(std::move(f1)); // !!! why DOES this one compile when it SHOULDN'T?
auto idx1 = v1.index(); //equals 1. WHY?

variant_t v2(std::move(f2)); // !!! why DOESN'T this one compile when it SHOULD?

这里是编译错误:

错误 C2665 'std::variant<:function> (int)>,std::function>::variant': 2 个重载都没有 可以转换所有参数类型

好的,让我们将variant的项目签名从返回void更改为int

using variant_t = std::variant<
    std::function<std::future<int>(int)>,
    std::function<int(int)>
>;

variant_t v1(std::move(f1)); // COMPILES (like it should)
auto idx1 = v1.index(); // equals 0

variant_t v2(std::move(f2)); // DOESN'T compile (like it should)

这到底是怎么回事?为什么std::future&lt;void&gt; 如此特别?

【问题讨论】:

  • 注意:std::variant 是 C++17 功能,而不是 C++11。
  • 您的编译错误来自第二个示例,但您的问题看起来像是来自您的第一个示例。

标签: c++ lambda future c++17 variant


【解决方案1】:

variant 的转换构造函数模板采用重载决议来确定构造对象应具有的类型。特别是,这意味着如果对这些类型的转换同样好,构造函数就不起作用;在您的情况下,如果您的论点恰好可以构造 std::function 专业化之一,它就可以工作。

那么function&lt;...&gt; 什么时候可以从给定的参数构造?从 C++14 开始,如果参数可以使用参数类型调用并生成 convertible to the return type 类型。请注意,根据本规范,如果返回类型为void,则一切正常(如any expression can be converted to void with static_cast)。如果你有一个function 返回void,你传入的函子可以返回任何东西——这是一个特性,而不是一个错误!这也是function&lt;void(int)&gt; 适用于f1 的原因。另一方面,future&lt;int&gt; 不会转换为future&lt;void&gt;;因此只有function&lt;void(int)&gt; 是可行的,并且变体的索引是1。

但是,在第二种情况下,lambda 返回future&lt;void&gt;,它可以转换为future&lt;void&gt;void。如上所述,这导致 function 的两个特化都可行,这就是 variant 无法决定构建哪个特化的原因。

最后,如果将返回类型调整为int,整个void 转换问题就可以避免,所以一切都按预期工作。

【讨论】:

  • 感谢您的回复。但我仍然无法想象std::future&lt;int&gt; 如何可以隐式转换为void(或void 转换为std::future&lt;int&gt; - 我不确定我是否理解这一点)?这是否意味着一切都可以转换为void
  • @DmitryKatkevich 没错,任何表达式都可以通过static_cast转换成void。否则,我们无法为function 提供函子,当function 产生void 时返回一些东西,这是一个理想的功能!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 2021-01-07
  • 1970-01-01
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
  • 2021-04-25
相关资源
最近更新 更多