4

If I keep a constant reference to a non-reference returned value of a function in C++11, where does the reference point in the stack? And is it safe to do so?

string foo() {
  std::string foo_ret = "foo string";
  return foo_ret;
}

int main() {
  const std::string& a = foo();
}
ebi
  • 501
  • 3
  • 12
  • its safe. temporary lifetime is extended because its bind to reference. (might want const reference though) – tp1 Aug 11 '15 at 23:59
  • 1
    It doesn't compile: `error: invalid initialization of non-const reference of type 'std::string& {aka std::basic_string&}' from an rvalue of type 'std::string {aka std::basic_string}'` – melpomene Aug 12 '15 at 00:01
  • 2
    @tp1 this is not safe and won't even compile – NathanOliver Aug 12 '15 at 00:05
  • "If I keep a reference of a non-reference" – unfortunately you can't do that. It is not valid C++. – Emil Laine Aug 12 '15 at 00:20
  • 1
    he's just missing const. Once you add that, it magically becomes valid c++. – tp1 Aug 12 '15 at 00:22
  • @tp1 Are you sure? Maybe he wants an rvalue reference and is just missing another `&`? Or maybe he really wants a non-const lvalue reference and his compiler is silently allowing him to do that, which made him think it's possible? – Emil Laine Aug 12 '15 at 00:24
  • @melpomene thanks, I've added `const`. – ebi Aug 12 '15 at 08:35

3 Answers3

5

Your code is illegal; non-const lvalue references may not bind to rvalues. There's not really a good reason behind this, it's just a language rule that was introduced very early on in C++'s history.
MSVC used to (maybe still does) allow this binding, I can't comment on how MSVC implements it.

You can bind to other reference types though:

std::string const &a = foo();   // (1)
std::string&& b = foo();        // (2)

In case (2), b binds directly to the return value object, which has its lifetime extended to match b's lifetime. Note: no "move" operation occurs here, it is just binding a reference.

In case (1), conceptually, a temporary of type const std::string is initialized from the return value, and that temporary has its lifetime extended to match a's lifetime. In practice this copy will be elided. your code will behave as if the reference bound directly to the return value.


Generally speaking, you should use value semantics. std::string c = foo(); is the safest option. Because of copy elision, it is not any less efficient than the reference options.

The main danger with the reference option is that if the function were changed to return a reference, then a or b may become a dangling reference.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • I've meant `const &`. So, in case (1) where the data is stored? heap or stack? – ebi Aug 12 '15 at 08:35
  • @ebi implementation-defined. It will probably pair up with NRVO, and construct the returned string directly inside `main()`'s stackframe. – Quentin Aug 12 '15 at 09:11
  • "(and maybe even more efficient)" – Could you add a "why" there? – Emil Laine Aug 12 '15 at 11:58
  • @ebi it could be stored anywhere, so long as the object continues to exist so long as `a` exists. The usual thing to do is that the object is in the stack area of the function containing `a`. – M.M Aug 12 '15 at 13:21
  • @zenith not really, it would be just hand-waving, so I've removed that comment – M.M Aug 12 '15 at 13:22
0

No it is not safe to do so, and it is an error and will not compile.

You might be looking for R Value references: http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html

Dominique McDonnell
  • 2,510
  • 16
  • 25
0

In my opinion, it is legal to to do.

When you return a string from the function, it returns a Rvalue or temporary object. You can not use normal Lvalue reference to fetch the Rvalue but you can use const Lvalue reference to fetch. When you use const Lvalue reference, the life time of the temporary object is extended to be the same as the reference does.

As for where the reference point to in the memory, I guess it may vary by implementation. But what matters here is that the temporary object is no longer 'temporary', although you can not change it.

Ming Li
  • 45
  • 3