The following code causes undefined behaviour:
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
}
(because lifetime extension by const&
doesn't apply here, as it's my understanding).
To prevent this, one solution might be to add a reference qualifier to get()
to prevent it being called on LValues:
const std::string& get() const & { return s_; }
However, because the function now is both const
and &
qualified, it is still possible to call get()
on RValues, because they can be assigned to const&
:
const auto& t = T{}; // OK
const auto& s1 = t.get(); // OK
const auto& s2 = T{}.get(); // OK <-- BAD
The only way to prevent this (as far as I can see) is to either overload get()
with a &&
-qualified variant that doesn't return a reference, or to = delete
it:
const std::string& get() const & { return s_; }
const std::string& get() const && = delete; // Var. 1
std::string get() const && { return s_; }; // Var. 2
However, this implies that to implement getter-functions that return (const) references correctly, I always have to provide either Var. 1 oder 2., which amounts to a lot of boilerplate code.
So my question is: Is there a better/leaner way to implement getter-funtions that return references, so that the compiler can identify/prevent the mentioned UB-case? Or is there a fundamental flaw in my understanding of the problem?
Also, so far I couldn't find an example where adding &
to a const
member function brings any advantages without also handling the &&
overload...maybe anyone can provide one, if it exists?
(I'm on MSVC 2019 v142 using C++17, if that makes any difference)
Thank you and best regards