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.
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.
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.
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.
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 .
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.
You can find the answer in the revision N3582 (2013-03-15)1 to the original proposal for auto
:
auto
in trailing-return-typeThis 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With