0
class AA {
private:
    string s = "asd";
public:
    string func1() {
        return s;
    }
    string& func2() {
        return s;
    }
};

func1() returns a copy and func2() returns a reference. And calls are like

AA a;
auto &r1 = a.func1();
auto &r2 = a.func2();

Both work fine after I test them. But here is my doubt. r2 refers to AA::s; I get it. But does r1 refer to AA::s too? Or func1's anonymous return value? If the former, how does func1 do it? RVO?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
f1msch
  • 509
  • 2
  • 12
  • func1() returns a copy of the string s (temporary), and r1 references this temporary. func2() returns a reference to the original string s, and r2 references the original string s. func1(), as the reference r1 will become a dangling reference once the temporary returned by func1() goes out of scope. In this specific example, since func1() returns a copy, it's not an issue. However, if func1() returned a reference to a non-static member variable of the object (which would be dangerous), it could lead to undefined behavior if the reference is used outside of its scope. – Skerdi Velo Jul 18 '23 at 09:14
  • 6
    `auto &r1 = a.func1();` doesn't compile: https://godbolt.org/z/s4T1K3qjE – UnholySheep Jul 18 '23 at 09:16
  • 3
    If it compiles (it shouldn't, but Visual C++ will do it) `r1` refers to a nonexistent object and only appears to "work" through the nature of undefined behaviour. (You can't determine the absence of undefined behaviour by testing.) – molbdnilo Jul 18 '23 at 09:16
  • @molbdnilo msvc supports r-value to l-value conversion https://stackoverflow.com/questions/11508607/rvalue-to-lvalue-conversion-visual-studio – Dmitry Jul 18 '23 at 09:24
  • 2
    `const auto &r1 = a.func1();` is legal. And it's not UB because it extends the lifetime of the returned object. – Rodney Jul 18 '23 at 09:33
  • 1
    @Dmitry Yes, unfortunately it does. It still does not extend the lifetime of the temporary, though. – molbdnilo Jul 18 '23 at 09:36
  • 3
    `Whenever a reference is bound to a temporary object or to a subobject thereof, the lifetime of the temporary object is extended to match the lifetime of the reference ` https://en.cppreference.com/w/cpp/language/reference_initialization – Rodney Jul 18 '23 at 09:38
  • 3
    If you are using Visual Studio (CL.EXE), I recommend using the `/permissive-` flag to disable Microsoft's extensions and alterations to standard C++, rather than using Microsoft's non-compliant C++. (I make a similar recommendation for GNU's GCC's `g++` and LLVM's `clang++`.) – Eljay Jul 18 '23 at 11:13
  • @Rodney, you're overlooking "_if the reference is lvalue reference to a non-volatile **const-qualified type** or rvalue reference_" that precedes that quote and applies to it. `r1` and `r2` are _not_ const-qualified here. – Toby Speight Jul 18 '23 at 13:25
  • Compiles fine when `const` is added: https://godbolt.org/z/3fqPcsfW4 – Marek R Jul 18 '23 at 13:31
  • @TobySpeight the comment was a follow up to the previous comment in which I added `const` to `r1`. It doesn't apply at all to `r2`. I am curious to know whether with the VS extension others have mentioned, the lifetime is also extended for non-const. If not then this extension would just pointlessly enable broken code. – Rodney Jul 20 '23 at 08:47

1 Answers1

1

Both work fine after I test them

No, the former auto &r1 = a.func1(); isn't legal/valid C++ since func1 returns by value and since placeholder type deduction uses the rules for template argument deduction. This means that the situation/problem is similar to the problem as if you were to write:

template<typename T> void deducefromauto(T &t);
deducefromauto(a.func1()); //won't work because non-const lvalue reference can't be bound to rvalue

And this won't work as a non-const lvalue reference can't be bound to an rvalue.

Jason
  • 36,170
  • 5
  • 26
  • 60