C++传入函数的右值能否延长生命周期?懒求值场景实现咨询
问题根源
C++的临时对象生命周期延长规则仅适用于临时对象直接绑定到局部引用变量的场景。你在构造函数参数中用右值引用绑定临时Number对象后,再将该引用赋值给类的成员引用,生命周期延长效果不会传递,临时对象会在LazyAddition构造函数调用结束后立即销毁,最终导致成员引用悬空。
可行实现方案
方案1:非模板实现(基于std::variant存储值/引用)
该方案无需引入模板,通过std::variant动态判断存储左值引用还是右值本身,右值通过移动构造存入,无拷贝开销:
#include <variant> #include <functional> class Number; class LazyAddition { using Storage = std::variant<std::reference_wrapper<Number>, Number>; Storage lhs; Storage rhs; static int get_value(const Storage& s) { if (std::holds_alternative<std::reference_wrapper<Number>>(s)) { return std::get<std::reference_wrapper<Number>>(s).get().x; } return std::get<Number>(s).x; } public: // 完美转发构造函数,自动适配所有左右值组合 template<typename L, typename R> LazyAddition(L&& l, R&& r) { // 处理左值存引用,右值移动存储 if constexpr (std::is_lvalue_reference_v<L>) { lhs = std::ref(l); } else { lhs = std::move(l); } if constexpr (std::is_lvalue_reference_v<R>) { rhs = std::ref(r); } else { rhs = std::move(r); } } friend class Number; }; class Number { public: Number(int x) : x(x) {} template<typename R> LazyAddition operator+(R&& rhs) { return LazyAddition(*this, std::forward<R>(rhs)); } Number(LazyAddition lazy) { x = lazy.get_value(lazy.lhs) + lazy.get_value(lazy.rhs); } private: int x; friend class LazyAddition; };
方案2:模板表达式实现(编译期确定存储类型)
如果可以接受模板实现,编译期就能确定每个参数的存储类型,运行时无额外开销,是懒表达式的常用实现方式:
#include <type_traits> #include <functional> class Number; template<typename L, typename R> class LazyAddition { L lhs; R rhs; public: LazyAddition(L l, R r) : lhs(std::move(l)), rhs(std::move(r)) {} int eval() const; friend class Number; }; class Number { public: Number(int x) : x(x) {} template<typename R> auto operator+(R&& rhs) { // 左值存引用包装,右值直接存值(移动构造) using LStorage = std::reference_wrapper<Number>; using RStorage = std::conditional_t< std::is_lvalue_reference_v<R>, std::reference_wrapper<std::remove_reference_t<R>>, std::remove_reference_t<R> >; return LazyAddition<LStorage, RStorage>(std::ref(*this), std::forward<R>(rhs)); } template<typename L, typename R> Number(LazyAddition<L, R> lazy) { x = lazy.eval(); } private: int x; template<typename L, typename R> friend class LazyAddition; }; template<typename L, typename R> int LazyAddition<L, R>::eval() const { auto get_val = [](const auto& val) -> int { if constexpr (std::is_same_v<std::decay_t<decltype(val)>, std::reference_wrapper<Number>>) { return val.get().x; } else { return val.x; } }; return get_val(lhs) + get_val(rhs); }
验证用例
两种方案均支持以下所有场景,无悬空引用也无多余拷贝:
// 左值场景 Number num1(3); Number num2(4); Number res1 = num1 + num2; // 右值场景 Number res2 = Number(3) + Number(4); // 混合左右值场景 Number res3 = num1 + Number(4); Number res4 = Number(3) + num2;
内容的提问来源于stack exchange,提问作者Jaden Amatuzzo
相关产品推荐
相关产品推荐

