c++ - 防弹 C++ 临时生命周期?

标签 c++ c++17 lifetime

重新审视 C++ 中的生命周期扩展,我发现有一些模式破坏了 C++ 表达式的“可分解性”。例如,以下两个 block 是 valid C++ code :

class NonMovable {
public:
   NonMovable(NonMovable&&) = delete;
   NonMovable(const NonMovable&) = delete;
   NonMovable();

   int Value() const;
};

template <class T>
const T& identity(const T& x) {
    return x;
}

template <class T>
class A {
public:
    explicit A(const T& value) : value_(value) {}
    const T& GetValue() const {
        return value_;
    }
private:
    const T& value_;
};

正确用法:

int main() {
    int retcode = identity(
        identity(/*tmp1*/ A(/*tmp2*/ NonMovable{}).GetValue())).Value();
    // tmp1 and tmp2 end their lifetimes here: 
    // their full-expression is the whole previous line
    return retcode;
}

但是如果我们分解main中的第一个表达式,它就会变得无效:

int main() {
    auto&& a_obj = /*tmp1*/ A(/*tmp2*/ NonMovable{});
    // tmp2 lifetime ends here

    // oops! dereferencing dangling reference:
    int retcode = identity(
        identity(a_obj.GetValue())).Value();
    return retcode;
    // tmp1 lifetime ends here
}

我的问题是: 是否可以禁用第二种用法?

P.S.:我不太确定第二个 main 是否引入了 UB,因为我已经 tested使用clang -Wlifetime,它不会提示。但我还是相信是UB。在现实生活中,我遇到过类似的行为:如果我将单个表达式分解为两个单独的表达式,代码就会损坏,发出 UBSan 警告和段错误。

P.P.S.:如果我正确理解对象的生命周期(我现在对此表示怀疑),那些身份实际上并不重要

最佳答案

你的分析是正确的。如果没有生命周期扩展,所有临时变量都会在“完整表达式”的末尾被销毁,即行末尾的 ; 。所以当你说

int retcode = A(NonMovable{}).GetValue().Value();

(为了清楚起见,删除了评论和身份调用)然后一切都好;当您请求其值时,NonMovable 对象仍然存在。

另一方面,当你说

auto&& a_obj = A(NonMovable{});

然后 NonMovable 在行尾被销毁,并且 A 对象将持有一个悬空引用。 (顺便说一句,auto&&只是在这里延长了临时A的生​​命周期——您也可以只使用普通的auto)

My question is: Is it possible to disable the second kind of usage?

事实并非如此,至少据我所知。您可以添加已删除的 A(NonMovable&&) 构造函数,但这也会阻止第一个示例中的“正确”使用。这与 std::string_view 出现的问题完全相同(并且在 C++20 中也会出现 std::span)——本质上,您的 类具有引用语义,但引用的是已被销毁的临时对象。

关于c++ - 防弹 C++ 临时生命周期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58166210/

相关文章:

c++ - 在 `if constexpr` block 内声明的变量范围

rust - 为什么这个 Rust 代码在 struct 上编译时带有生命周期限制,但如果边界仅在 impl 上,则会给出生命周期错误?

c++ - 如何将 C++ 枚举复制到 Objective C 枚举中

c++ - 为什么 Xcode 11 beta 不能使用 C++17 的 <filesystem> header ?

c++ - 使用 cin.get 获取整数

c++ - 隐藏 constexpr 变量

rust - 闭合体的生命周期与传递给它的值之间不匹配

rust - lazy_static 可变变量的生命周期问题

c++ - 删除重复链接列表

c++ - 条件语句简化 - C++