假设我有一个跨平台的 Path
类:
class Path {
public:
// ...
Path parent() const; // e.g., /foo/bar -> /foo
std::string const& as_utf8() const {
return path;
}
private:
std::string path;
};
parent()
成员函数返回this
路径的父路径,因此它(正确地)返回一个新构造的Path
对象代表它。
对于将操作系统级别的路径表示为 UTF-8 字符串的平台(例如,Unix),as_utf8()
直接返回对内部表示的引用似乎是合理的 path
因为它已经是 UTF-8。
如果我有这样的代码:
std::string const &s = my_path.as_utf8(); // OK (as long as my_path exists)
// ...
Path const &parent = my_path.parent(); // OK (temporary lifetime extended)
这两行都很好,因为:
- 假设
my_path
持续存在,则s
仍然有效。 parent()
返回的临时对象的生命周期由const&
延长。
到目前为止,还不错。但是,如果我有这样的代码:
std::string const &s = my_path.parent().as_utf8(); // WRONG
那么这是错误的,因为parent()
返回的临时对象没有延长了它的生命周期,因为const&
不不 引用临时文件,而是引用它的数据成员。此时,如果您尝试使用 s
,您将得到垃圾或核心转储。如果代码是:
std::string as_utf8() const { // Note: object and NOT const&
return path;
}
那么代码就是正确的。但是,每次调用此成员函数时都创建一个临时对象是低效的。这也意味着没有“getter”成员函数应该永远返回对其数据成员的引用。
如果 API 保持原样,那么调用者必须查看 as_utf8()
的返回类型以查看它是否返回 const&
或不是:如果是,则调用者必须使用对象并且不是 const&
;如果它返回一个对象,那么调用者可以使用const&
。
那么有什么方法可以解决这个问题,使 API 在大多数情况下既高效又能防止用户从看似无害的代码中获取悬挂引用?
顺便说一下,这是使用 g++ 5.3 编译的。有可能应该延长临时文件的生命周期,但编译器存在错误。
最佳答案
您可以做的是创建 2 个不同版本的 as_utf8()
,一个用于左值,一个用于右值。不过,您需要 C++11。
这样,您就可以两全其美:当对象不是临时对象时使用 const&
,而当对象不是临时对象时使用高效移动:
std::string const& as_utf8() const & {
// ^^^ Called from lvalues only
return path;
}
std::string as_utf8() const && {
// ^^^^ Called from rvalues only
return std::move(path); //We don't need path any more
}
关于c++ - 临时数据成员的生命周期延长和API设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39128193/