我正在玩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?
以下是编译错误:
错误C2665'std :: variant&lt; std :: function&lt; std :: future&lt; void&gt; (int)&gt ;,std :: function&lt; void(int)&gt;&gt; :: 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<void>
如此特别?
答案 0 :(得分:16)
variant
的转换构造函数模板使用重载决策来确定构造对象应该具有哪种类型。特别是,这意味着如果对这些类型的转换同样好,则构造函数不起作用;在你的情况下,如果std::function
特化中只有一个可以从你的参数中构造出来,那么它就可以工作。
那么function<...>
何时可以从给定的参数中构造出来?从C ++ 14开始,如果参数可以使用参数类型调用,并生成一个convertible to the return type的类型。请注意,根据此规范,如果返回类型为void
,则任何内容都会变为(any expression can be converted to void
with static_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
转换问题,因此一切都按预期工作。