无法使用各种Lambda表达式初始化std :: variant [英] Cannot initialize std::variant with various lambda expressions
问题描述
我正在玩std::variant, lambdas
和std::future
,当我尝试将它们组合在一起时得到了超级奇怪的结果.以下是示例:
I'm playing with std::variant, lambdas
and std::future
, and got super weird results when I tried to compose them together. Here are examples:
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< std :: function< std :: future< void> (int)>,std :: function< void(int)>> :: variant':2个重载都不 可以转换所有参数类型
Error C2665 'std::variant<std::function<std::future<void> (int)>,std::function<void (int)>>::variant': none of the 2 overloads could convert all the argument types
好,让我们将variant
的项目签名从void
返回到int
:
OK, lets change variant
's items signatures from returning void
to 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>
如此特别?
What the hell is going on here? Why is std::future<void>
so special?
推荐答案
variant
的转换构造函数模板使用重载分辨率来确定所构造的对象应具有的类型.特别是,这意味着,如果对这些类型的转换同样出色,则构造函数将无法工作.在您的情况下,如果完全可以根据您的论证构造std::function
专业化之一,它就可以工作.
variant
's converting constructor template employs overload resolution to determine which type the constructed object should have. In particular, this means that if the conversions to those types are equally good, the constructor doesn't work; in your case, it works iff exactly one of the std::function
specializations is constructible from your argument.
那么什么时候function<...>
可以从给定的参数构造出来?从C ++ 14开始,如果参数可以使用参数类型调用,并且产生的类型为可转换为返回类型.请注意,根据此规范,如果返回类型为void
,则任何操作都会进行(如
So when is function<...>
constructible from a given argument? As of C++14, if the argument is callable with the parameter types and yields a type that is convertible to the return type. Note that according to this specification, if the return type is void
, anything goes (as any expression can be converted to void
with static_cast
). If you have a function
returning void
, the functor you pass in can return anything—that's a feature, not a bug! This is also why function<void(int)>
is applicable for f1
. On the other hand, future<int>
does not convert to future<void>
; hence only function<void(int)>
is viable, and the variant's index is 1.
但是,在第二种情况下,lambda返回future<void>
,它可以转换为future<void>
和void
.如上所述,这使得function
的两个专业都可行,这就是variant
无法确定要构建哪个专业的原因.
However, in the second case, the lambda returns future<void>
, which is convertible to both future<void>
and void
. As mentioned above, this causes both function
specializations to be viable, which is why the variant
cannot decide which one to construct.
最后,如果将返回类型调整为int
,则可以避免整个void
转换问题,因此一切都会按预期进行.
Finally, if you adjust the return type to int
, this whole void
conversion issue is avoided, so everything works as expected.
这篇关于无法使用各种Lambda表达式初始化std :: variant的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!