Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

decltype(auto) type deduction: return x vs. return (x)

I'm looking at the isocpp.org FAQ on C++14 language extensions, reading about decltype(auto):

...

Note: decltype(auto) is primarily useful for deducing the return type of forwarding functions and similar wrappers, as shown above, where you want the type to exactly “track” some expression you’re invoking. However, decltype(auto) is not intended to be a widely used feature beyond that. In particular, although it can be used to declare local variables, doing that is probably just an antipattern since a local variable’s reference-ness should not depend on the initialization expression. Also, it is sensitive to how you write the return statement. These two functions have different return types:

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

The first returns string, the second returns string &, which is a reference to the local variable str.

My question: Shouldn't the return types in the example be the other way around, I mean, the parentheses should form an expression whose type should be either a non-reference (or an rvalue-reference?); and without the parentheses, saying str means "lvalue reference to str". Am I wrong?

like image 837
einpoklum Avatar asked Jan 27 '23 10:01

einpoklum


2 Answers

Yes the parentheses form an expression. Expressions are not of reference type, ever.

If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.

"reference to T" means both lvalue and rvalue reference. But the expression does have a value category. str as an expression is an lvalue, and so is (str) since parentheses preserve type and value category.

Since the decltype is applied to a lvalue expression in the second case, it produces a lvalue reference.

In the first case, the special pleading rule for id-expressions returns the "type of the entity", which is a value type.

like image 117
StoryTeller - Unslander Monica Avatar answered Feb 11 '23 06:02

StoryTeller - Unslander Monica


The FAQ is correct, intuition notwithstanding

(@lubgr pointed out a relevant answer to another question)

The language specification says:

If the argument is either the unparenthesised name of an object/function ... then the decltype specifies the declared type of the entity specified by this expression.

If the argument is any other expression of type T, then ... b) if the value category of expression is lvalue, then the decltype specifies T& ... Note that if the name of an object is parenthesised, it becomes an lvalue expression, thus decltype(arg) and decltype((arg)) are often different types.

So, the non-parenthesized case is an exceptional/special case, apparently introduced to facilitate returning references with decltype(auto); without the special-casing, the rule @StoryTeller quotes is in effect:

If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis.

and this would have make it difficult to return references.

Definitely ranks up there with other magick definitions such as the destruction order of tuples or empty strings having 0 at index 0, temporary lifetime extension with references and other similar wonders...

like image 25
einpoklum Avatar answered Feb 11 '23 04:02

einpoklum