C++20中如何使用ref qualifier兼顾代码安全与可用性?
问题与解决方案
场景代码
#include <string> struct S { std::string s_; std::string_view get() const & { return s_; } std::string_view get() const && = delete; }; // 我无法修改这段代码 struct T { T(std::string_view const sv); // 用sv执行安全操作 }; // 我无法修改这段代码 void f(std::string_view const &); void g() { f(S{}.get()); // 应当通过编译 T t(S{}.get()); // 应当通过编译 std::string_view sv = S{}.get(); // 应当编译失败 }
需求
使用ref qualifier时,要同时满足:
- 禁止
std::string_view sv = S{}.get()这类不安全用法(临时对象销毁后string_view会悬空) - 让
f(S{}.get())、T t(S{}.get())这类仅在临时对象生命周期内使用string_view的安全操作正常编译 - 不能通过包装
f来实现,避免冗余代码
可行方案:返回受限的string_view包装类
核心思路是给get()返回一个定制的包装类型,精准控制它能转换到哪些类型、在什么场景下转换,既能适配安全的传参场景,又能阻止危险的赋值操作。
实现代码
#include <string> // 定制包装类,控制string_view的安全传递 class SafeStringView { std::string_view sv_; public: explicit SafeStringView(std::string_view sv) noexcept : sv_(sv) {} // 适配f的const&参数:允许隐式转成const string_view& operator std::string_view const&() const noexcept { return sv_; } // 适配T的传值构造:允许右值转成string_view值 operator std::string_view() const && noexcept { return sv_; } // 禁止左值转成string_view值,阻止直接赋值给变量 operator std::string_view() const & = delete; }; struct S { std::string s_; SafeStringView get() const & noexcept { return SafeStringView(s_); } // 临时对象可以调用get,但返回的包装类只能安全使用 SafeStringView get() const && noexcept { return SafeStringView(s_); } }; // 原有不可修改的代码保持原样 struct T { T(std::string_view const sv); }; void f(std::string_view const &); void g() { f(S{}.get()); // 编译通过:隐式转成const string_view& T t(S{}.get()); // 编译通过:右值转成string_view值 std::string_view sv = S{}.get(); // 编译失败:左值转值类型被禁用 }
方案细节
- 精准控制转换场景:
当SafeStringView作为左值(比如赋值给变量)时,只能转换为std::string_view const&,但直接转成string_view值的重载被删除,所以危险的赋值操作直接编译报错。
当作为右值(直接传参)时,允许转成string_view值,完美适配f的引用参数和T的传值构造函数。 - 零侵入原有代码:不需要修改
f和T的任何代码,包装类的隐式转换完全匹配它们的参数要求,不用写冗余的包装函数。 - 保障生命周期安全:临时对象
S{}的生命周期会延续到整个表达式结束,所以f(S{}.get())和T t(S{}.get())中的string_view不会悬空,操作完全安全。
内容的提问来源于stack exchange,提问作者zrb
相关产品推荐
相关产品推荐

