c++ - 从 lambda (C++) 创建的 std::function 的奇怪返回行为

标签 c++ c++11 lambda g++ std-function

如果函数返回引用但返回类型未显式调用为引用,则我在使用从 lambdas 创建的 std::functions 时遇到问题。似乎 std::function 没有警告就创建得很好,但是在调用它时,会在需要引用时返回一个值,从而导致事情崩溃。这是一个非常人为的例子:

#include <iostream>
#include <vector>
#include <functional>

int main(){
   std::vector<int> v;
   v.push_back(123);
   std::function<const std::vector<int>&(const std::vector<int>&)> callback =
      [](const std::vector<int> &in){return in;};
   std::cout << callback(v).at(0) << std::endl;
   return 0;
}

这会打印出垃圾,但是如果将 lambda 修改为显式返回 const 引用,它就可以正常工作。我可以理解编译器认为 lambda 是按值返回而没有提示(当我最初遇到这个问题时,lambda 直接从返回 const 引用的函数返回结果,在这种情况下,我认为lambda 的 const 引用返回是可推导出的,但显然不是。)令我惊讶的是编译器允许 std::function 从具有不匹配返回类型的 lambda 构造。这种行为是预期的吗?我是否遗漏了标准中允许发生这种不匹配的内容?我在 g++ (GCC) 4.8.2 中看到了这个,没有用其他任何东西尝试过。

谢谢!

最佳答案

为什么坏了?

当推导 lambda 的返回类型时,引用和 cv 限定被删除。所以

的返回类型
[](const std::vector<int> &in){return in;};

只是 std::vector<int> ,而不是 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返回一个临时的。它实际上只是复制了它的输入。这个临时绑定(bind)在 callback 中的引用返回.但是临时对象绑定(bind)到 return 中的引用语句没有延长它们的生命周期,因此临时在 return 语句结束时被销毁。因此,此时:

callback(v).at(0)
-----------^

我们有一个对 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 is Callable (20.9.12.2) for argument types ArgTypes... and return type R.

哪里,[func.wrap.func]:

A callable object f of type F is Callable for argument types ArgTypes and return type R if the expression INVOKE (f, declval<ArgTypes>()..., R), considered as an unevaluated operand (Clause 5), is well formed (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, otherwise INVOKE(f, t1, t2, ..., tN) implicitly converted to R.

所以,如果我们有:

T func();
std::function<T const&()> wrapped(func);

这实际上符合所有标准要求:INVOKE(func)格式正确,虽然它返回 T , T 可隐式转换为 T const& .所以这不是 gcc 或 clang 错误。这可能是一个标准缺陷,因为我不明白您为什么要允许这样的构造。这将永远有效,因此措辞可能要求如果 R是引用类型,那么 F也必须返回一个引用类型。

关于c++ - 从 lambda (C++) 创建的 std::function 的奇怪返回行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32871606/

相关文章:

java - 从匿名类到 lambda 表达式

python - 如何为 functool partial() 指定 arg 位置

c++ - 指向整个数组的指针

c++ - 为什么用大括号 init 初始化对象是合法的,即使它不是聚合?

c++ - 使用可变参数宏打印多个调试行

c++ - 仍在执行时调用 std::function 析构函数

python - 比较 2 个数据框中每列有多少个不同值

c++ - 微 Controller 上 C 和 asm 的替代品

c# - 找到最好的区间匹配结果

c++ - 我可以禁用静态断言吗?