无法使用各种lambda表达式初始化std :: variant

Dmi*_*ich 15 c++ lambda future variant c++17

我正在玩,std::variant, lambdas并且std::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?
Run Code Online (Sandbox Code Playgroud)

这是编译错误:

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

OK,让改变variant从返回的项目签字voidint:

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

这到底是怎么回事?为什么std::future<void>这么特别?

Col*_*mbo 16

variant转换构造函数模板采用重载决策来确定构造对象应该具有哪种类型.特别是,这意味着如果对这些类型的转换同样好,则构造函数不起作用; 在你的情况下,如果其中一个特化std::function是可以从你的参数构造的,那么它是有效的.

那么什么时候可以function<...>从给定的参数构造?从C++ 14开始,如果参数可以使用参数类型调用,并生成一个可转换为返回类型的类型.请注意,根据此规范,如果返回类型是void,则任何内容都会发生(因为任何表达式都可以转换为voidwithstatic_cast).如果你有一个function返回void,你传入的仿函数可以返回任何东西 - 这是一个功能,而不是一个错误!这也是function<void(int)>适用的原因f1.另一方面, future<int>不转换为future<void>; 因此只有function<void(int)>可行,变量的索引是1.

但是,在第二种情况下,lambda返回future<void>,可以转换为future<void>void.如上所述,这导致两种特化都是function可行的,这就是为什么variant不能决定构建哪一种.

最后,如果您将返回类型调整为int,void则避免了整个转换问题,因此一切都按预期工作.

  • @DmitryKatkevich这是正确的,任何表达式都可以通过`static_cast`转换为`void`.否则,我们无法向`function`提供函数,当`function`产生`void`时返回一些东西,这是一个理想的特性! (3认同)