7

以下代码导致未定义的行为:

class T
{
public:
    const std::string& get() const { return s_; }

private:
    std::string s_ { "test" };
}

void breaking()
{
    const auto& str = T{}.get();
    // do sth with "str" <-- UB
}

(因为我的理解是延长寿命const&在这里不适用)。

为了防止这种情况,一种解决方案可能是添加一个引用限定符get()以防止它在 LValues 上被调用:

const std::string& get() const & { return s_; }

但是,由于现在的函数既是合格的const&所以仍然可以调用get()RValues,因为它们可以分配给const&

const auto& t = T{};        // OK
const auto& s1 = t.get();   // OK
const auto& s2 = T{}.get(); // OK <-- BAD

防止这种情况(据我所知)的唯一方法是使用不返回引用get()的-qualified 变体重载,或者重载它:&&= delete

const std::string& get() const &  { return s_; }

const std::string& get() const && = delete;       // Var. 1
      std::string  get() const && { return s_; }; // Var. 2

但是,这意味着要实现正确返回 (const) 引用的 getter 函数,我总是必须提供 Var。1 oder 2.,这相当于很多样板代码。

所以我的问题是:是否有更好/更精简的方法来实现返回引用的 getter 函数,以便编译器可以识别/防止提到的 UB 案例?还是我对问题的理解存在根本缺陷?

另外,到目前为止,我还没有找到一个例子,在不处理重载的情况下添加&const成员函数会带来任何好处&&......也许任何人都可以提供一个,如果存在的话?

(我在使用 C++17 的 MSVC 2019 v142 上,如果这有什么不同的话)

感谢你并致以真诚的问候

4

1 回答 1

0

有点不清楚您正在使用哪些限制。如果它是一个选项,你可以摆脱 getter(s),让生命周期扩展来做它的事情:

struct T
{
    std::string s_ { "test" };
};

const auto& str = T{}.s_; // OK; lifetime extended

对于 getter,您可以选择 1. 提供重复的 getter 或 2. 接受调用者必须小心不要假设来自临时 getter 的引用仍然有效。如问题所示。


通过使用共享所有权,您可以保持私有访问,同时仍然使生命周期管理变得容易:

class T
{
    std::shared_ptr<std::string> s = std::make_shared<std::string>("test");
public:
    // alternatively std::weak_ptr
    const std::shared_ptr<const std::string>
    get() const {
        return s;
    }
};

但是您必须考虑运行时成本是否值得轻松。

于 2021-08-23T12:08:18.027 回答