【问题标题】:Why does returning const reference from a lambda result in a temporary?为什么从 lambda 返回 const 引用会导致临时结果?
【发布时间】:2018-05-04 19:56:05
【问题描述】:

我有一个成员返回const& 的情况,然后此结果在具有相同返回类型的 lambda 中转发。

MSVC2017 将此情况确定为有风险,并发出警告:returning address of local variable or temporary。使用 clang 和其他编译器进行的实证测试表明,这在所有方面都是正确的。我不明白的是,为什么这与返回相同类型的几个方法调用不同。

例如,这很有效:

class A {
public:
    const std::string& name() const { return m_name; }
private:
    std::string m_name;
};

class B {
public:
    const std::string& name() const { return m_a.name(); }
private:
    A m_a;
};

//...
B b;
std::cout << b.name();

按预期工作,在编译或运行时没有警告/错误。

但是对于 lambda,它不会:

class A {
public:
    const std::string& name() const { return m_name; }
private:
    std::string m_name;
};

//...
using Getter = std::function< const std::string&() >;
A a;
Getter g = [&a] { return a.name(); };
std::cout << g();

导致崩溃,或至少打印损坏的内存

有人可以告诉我一些关于为什么这不起作用的信息吗?我通常希望它能够正常工作......

【问题讨论】:

  • 请注意,在c++17 中,我会将std::string const&amp; 替换为std::string_view,并且神奇地您的代码可以在零运行时成本和您预期的语义下正常工作。
  • 不是您问题的答案,而是:您只有有时需要std::function(例如,在同一个容器中包装和存储多个 lambda)。其余时间,只需 auto g = 就完全没问题(而且性能稍微好一点)。
  • @Rakete1111 不,因为 lambda 将返回 视图的副本而不是 字符串的副本,一切都像彩虹和独角兽一样工作.
  • @Yakk 啊,所以你想替换 每个 使用 const std::string&amp;。 :)
  • @Rakete1111 是的,作为一般哲学立场,每个const std::string&amp; 都应该替换为std::string_view。但也在 OP 的问题中,它修复了作为副作用的错误。

标签: c++ lambda reference c++14 constants


【解决方案1】:

您的 lambda 的返回类型 不是 引用。这就是你所有问题的原因。

您的 lambda 返回名称的副本。您将此 lambda 存储在 std::function 返回 const std::string&amp; 中,这意味着实际上,您将返回对该副本的引用,该副本将在 std::function 的调用运算符返回时立即被销毁!1

当然,解决方法是更改​​ lambda 的返回类型:

Getter g = [&a]() -> const std::string& { return a.name(); };

// or
Getter g = [&a]() -> auto& { return a.name(); };
// or if you are feeling fancy :P
Getter g = [&a]() -> decltype(auto) { return a.name(); };

1:为了扩展一点,您可以将std::function 的实现想象成这样(仅显示相关部分并进行了大量简化):

template<typename R, typename... Ts>
struct function<R(Ts...)> {
  R operator()(Ts... Args) const {
    return callInternalFunctionObject(Args...); // here: copy will get destructed
  }
};
// R=const std::string&, and Ts is empty

【讨论】:

  • 所以[&amp;a]() -&gt; decltype(auto) { return a.name(); } 返回一个引用而不是[&amp;a]() { return a.name(); }?我本来希望没有指定返回类型时的默认值是decltype(auto)
  • @agbinfo 啊不,是auto :)
  • 使其工作的另一种方法是使用auto &amp; 作为返回类型——问题是auto 永远不会推断引用,除非您明确告诉它(使用额外的@987654333 @ 或 decltype)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-20
相关资源
最近更新 更多