Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of a placeholder type in a trailing-return-type?

According to [dcl.fct]/2 the snippet below is legal. GCC and clang compile and execute the code,

#include <iostream>
int i = -1;
auto f()->auto&& { return i; }
int main(){
    f() = 2;
    std::cout << i << '\n';
}

printing

2

But what is the purpose of allowing this in C++?

In the example above, one could get the same result just by replacing the trailing-return-type with int&. In other words, I'm looking for an example where the trailing-return-type containing a placeholder type would be meaningful.

like image 746
WaldB Avatar asked Jan 29 '19 20:01

WaldB


People also ask

Why trailing return type?

The trailing return type feature removes a C++ limitation where the return type of a function template cannot be generalized if the return type depends on the types of the function arguments.

What is the return type of a function C++?

The return type, which specifies the type of the value that the function returns, or void if no value is returned. In C++11, auto is a valid return type that instructs the compiler to infer the type from the return statement. In C++14, decltype(auto) is also allowed.

What does decltype auto do?

Use auto and decltype to declare a function template whose return type depends on the types of its template arguments. Or, use auto and decltype to declare a function template that wraps a call to another function, and then returns the return type of the wrapped function.

Which is the default return type specifier in C++?

Every function declaration and definition must specify a return type, whether or not it actually returns a value. If a function declaration does not specify a return type, the compiler assumes an implicit return type of int .


2 Answers

You can make an argument about consistency: you can stick other types as trailing return types, why not placeholders?

auto f() -> int&  { return i; }
auto f() -> auto& { return i; }

You can make an argument about utility: the return type for lambdas looks like a trailing return type and has no other place to put a placeholder type, so you have to allow it for lambdas anyway, so might as well allow it for functions?

auto f = []() -> int&  { return i; };
auto f = []() -> auto& { return i; };

You can make an argument about code formatting. The trailing return type allows for consistent way to declare functions that always works for all cases, so just lining it up:

auto g(auto x)     -> decltype(f(x)) { ... } // using trailing for parameter
auto Cls::member() -> type { ... }  // using trailing for scope (to find Cls::type)
auto h(auto x)     -> auto& { ... }  // using trailing for formatting

There might be other arguments. But in short, it's easy to allow and clearly has merit.

like image 144
Barry Avatar answered Sep 28 '22 08:09

Barry


You can find the answer in the revision N3582 (2013-03-15)1 to the original proposal for auto:

auto in trailing-return-type

This proposal initially did not allow auto in a trailing-return-type, but since then it was pointed out that putting it there is the only way to specify that a lambda returns by a deduced reference type:

[]()->auto& { return f(); }

(Remember that not only functions but also lambdas can have a trailing-return-type)

Hence [dcl.spec.auto]/2:

The auto type-specifier may appear with a function declarator with a trailing-return-type ([dcl.fct]) in any context where such a declarator is valid.


1 Note: N3582 has been superseded by N3638 before it was actually adopted.

like image 44
rustyx Avatar answered Sep 28 '22 08:09

rustyx