4

In C++ you are allowed to write a return statement that looks like :

return ( ... );

which is different from the more popular :

return ... ;

In particular the first version returns the address/reference of something that is local to the stack of the function which contains that return statement.

Now why something would like to return a reference to something that, at that point, has no lifetime ?

What are the use case for this idiom ? Considering the new buzzword and features from C++11 and C++14 there is a different usage for this ?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
user2485710
  • 9,451
  • 13
  • 58
  • 102
  • 3
    It only returns a reference if the return type is a reference or `decltype(auto)`. I mean, it's perfectly valid to do `decltype(auto) vector::operator[](size_t index) {return (data_[index]);}` – chris Jun 09 '14 at 13:51
  • 5
    re "the first version returns the address/reference of something that is local to the stack of the function which contains that return statement.", no there is no connection. – Cheers and hth. - Alf Jun 09 '14 at 13:52
  • @chris yes, I forgot to add that part, but still this 2 idioms behave very differently . – user2485710 Jun 09 '14 at 13:52
  • 6
    @user2485710: how about providing a concrete example of different behavior. – Cheers and hth. - Alf Jun 09 '14 at 13:53
  • @user2485710 chris has provided an answer to the *why would you want to do this* part too - there's nothing wrong with it if the lifetime of the referent will outlive the function call. – Praetorian Jun 09 '14 at 13:55
  • @Praetorian, Whether you should do it for the sake of doing it is another matter. I'd love to hear the opinions Scott Meyer and Herb Sutter have on using `auto` and `decltype(auto)` specifically for function return types. – chris Jun 09 '14 at 13:56
  • @Cheersandhth.-Alf If I provide an example it will be the end of this question, because everyone's focus will be on a specific example rather then the more generic case. – user2485710 Jun 09 '14 at 13:58
  • possible duplicate of [When do extra parentheses have an effect, other than on operator precedence?](http://stackoverflow.com/questions/24116817/when-do-extra-parentheses-have-an-effect-other-than-on-operator-precedence) – Csq Jun 09 '14 at 14:00
  • 2
    @user2485710: **thanks**. I learned something new. At one very abstract level I knew, but I never ran into this (not using any C++14 compiler) and never considered this particular implication. Csq is possibly right about duplicate, but I think this particular example deserves its own question and answer, rather than people inferring it from general rules, which e.g. I had not done. – Cheers and hth. - Alf Jun 09 '14 at 14:05
  • @Csq that answer is even more generic, I would like at least 1 use case where this thing makes sense like when you can write `decltype(auto) foo(...) {return (...);} ` – user2485710 Jun 09 '14 at 14:05
  • user2485710 - the example of @chris is not enough? – Csq Jun 09 '14 at 14:11
  • You should include C++03 and earlier as this syntax has always been around since the first C++ revision. It is a carry over from the C language. – Thomas Matthews Jun 09 '14 at 14:17
  • @ThomasMatthews well the answer for C++11 covers C++03 as well, we only see a difference in C++1y. – Shafik Yaghmour Jun 10 '14 at 13:42

3 Answers3

13

The form is return expression;
Expression can be anything, including a parenthesised expression.
These are not different forms of returns, however, together
with decltype(auto) different types will be deduced.

sp2danny
  • 7,488
  • 3
  • 31
  • 53
  • yes but why you want to return a reference that is local to a function ? the focus of my question is about why you want to use the form `return (...);` – user2485710 Jun 09 '14 at 13:57
  • AFAIK there are slight differences in returning an expression and a const reference regarding possible optimization. But, I also think I have read somewhere in the specification that a braced expression is interpreted as equivalent to the unbraced one. Okay maybe the autotype thing is a different story. – Tobias Jun 09 '14 at 14:01
  • 2
    I wonder why this was upvoted to +7 when this answer is clearly incorrect. – Csq Jun 09 '14 at 14:12
  • 4
    @Csq because you decided to not describe why the answer was clearly incorrect? The expressions in question do differ, but they are the same form of `return` itself. The answer is incomplete, but I do not see where it is **incorrect**. – Yakk - Adam Nevraumont Jun 09 '14 at 14:20
  • @Csq, As far as I know, nothing stated in the answer is wrong. There's only one form of `return`. – chris Jun 09 '14 at 14:21
  • @chris sorry, the answer is correct. I really should have uses the word incomplete (5 mins passed, can't edit. should I delete that comment?). But the other answer is better as it highlights some differences that do happen if you use parentheses. – Csq Jun 09 '14 at 14:27
  • @Csq, No worries. I figured you might mean something like that. – chris Jun 09 '14 at 14:46
  • @chris IMO it's as irrelevant as an answer "The form of an initializer is 'initializer', and 'initializer' can be anything, including a braced initializer list" to the question "why would someone write `= { ...}` instead of `= ...` ? IMO it's a bad answer, because it's irrelevant (it *might* be not incorrect, but it surely doesn't answer any of the questions raised by the question). – Johannes Schaub - litb Jun 15 '14 at 12:29
10

The two versions differ in context when automatic return type deduction with decltype(auto) is used in C++14

Particularly the second is an antipatern in case B ( example taken from the C++FAQ)

decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }  //A
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); } //B

as it returns string& (as opposed to a string in A), which is a reference to the local variable str.

Community
  • 1
  • 1
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • And an use case will be ... ? I mean I can't really focus myself on a practical example where the signature `decltype(auto) foo( ... )` and `return ( ... )` will both be convenient and functional. – user2485710 Jun 09 '14 at 14:01
  • @user2485710 As stated, case B is an **antipattern**; A pitfall to avoid and a mess you might get yourself into if you widely use this feature. For "good" uses of `decltype(auto)` you can check my linked question. – Nikos Athanasiou Jun 09 '14 at 14:04
  • @user2485710: It's a special case of general rules, a special case that you want to *avoid*. ;-) The C++ rules in general do not make exemptions for possibly dangerous meaningless special cases. E.g., you can initialize a variable with its own value, so that you have an initialized variable with indeterminate value, and it could in theory have been singled out as invalid. But there are so many such cases. It would complicate the standard to an extreme degree. – Cheers and hth. - Alf Jun 09 '14 at 14:07
  • tnx, and +1. added this example to [this Q&A](http://stackoverflow.com/a/24116818/819272) – TemplateRex Jun 09 '14 at 15:45
8

Pre C++1y the parenthesized version of the return is identical, if we look at the C++11 draft standard section 6.6 Jump statements, the grammar for return is:

return expressionopt ;

return braced-init-list ;

an expression can be any expression and we can see from section 5.1 Primary expressions says (emphasis mine going forward):

A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue. The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.

In C++1y we can use delctype(auto) to deduce return types and this changes the situation as we can see from the draft C++1y standard section 7.1.6.4 auto specifier says:

When a variable declared using a placeholder type is initialized, or a return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer.[...]

and contains the following examples:

auto x3a = i; // decltype(x3a) is int

decltype(auto) x3d = i; // decltype(x3d) is int

auto x4a = (i); // decltype(x4a) is int

decltype(auto) x4d = (i); // decltype(x4d) is int&

and we can see there is a difference when using delctype(auto) and parenthesized expressions. The rule that is being applied is from section 7.1.6.2 Simple type specifiers paragraph 4 which says:

For an expression e, the type denoted by decltype(e) is defined as follows:

and includes the following bullets:

— if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

and:

— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740