为什么它坏了?
当推导 lambda 的返回类型时,引用和 cv 限定将被删除。所以返回类型为
[](const std::vector<int> &in){return in;};
is just std::vector<int>
, not std::vector<int> const&
。结果,如果我们去掉 lambda 并std::function
作为您代码的一部分,我们实际上拥有:
std::vector<int> lambda(std::vector<int> const& in)
{
return in;
}
std::vector<int> const& callback(std::vector<int> const& in)
{
return lambda(in);
}
lambda
返回一个临时的。它实际上只是复制其输入。这个临时绑定了引用返回callback
。但是临时变量绑定到 a 中的引用return
语句的生命周期没有延长,因此临时变量在 return 语句结束时被销毁。因此,此时:
callback(v).at(0)
-----------^
我们有一个对被破坏的悬空引用copy of v
.
解决方案是显式指定 lambda 的返回类型为引用:
[](const std::vector<int> &in)-> const std::vector<int>& {return in;}
[](const std::vector<int> &in)-> decltype(auto) {return in;} // C++14
现在没有副本,没有临时对象,没有悬空引用,也没有未定义的行为。
谁对谁错?
至于这是否是预期的行为,答案实际上是肯定的。一、可施工条件std::function
是[func.wrap.func.con]:
f
对于参数类型是可调用的 (20.9.12.2)ArgTypes...
和返回类型R
.
其中,[func.wrap.func]:
可调用对象f
类型的F
is Callable对于参数类型ArgTypes
和返回类型R
如果表达式INVOKE (f, declval<ArgTypes>()..., R)
,被视为未评估的操作数(第 5 条),很好
成立(20.9.2)。
其中,[func.require],强调我的:
Define INVOKE(f, t1, t2, ..., tN, R)
as static_cast<void>(INVOKE (f, t1, t2, ..., tN))
if R
is cv void
, 否则INVOKE(f, t1, t2, ..., tN)
隐式转换为R
.
所以,如果我们有:
T func();
std::function<T const&()> wrapped(func);
这实际上满足了所有标准要求:INVOKE(func)
格式良好,当它返回时T
, T
is隐式转换为T const&
。所以这不是 gcc 或 clang 错误。这可能是一个标准缺陷,因为我不明白为什么你会想要允许这样的构造。这会never是有效的,因此措辞可能会要求如果R
那么是一个引用类型F
还必须返回一个引用类型。