What is the advantage of specifying a trailing return type in C++11, as opposed to a normal return type? Look at foo1
vs foo2
here:
int foo1() {
return 1;
}
auto foo2() -> int {
return 1;
}
int main() {
foo1();
foo2();
}
In this example, they mean the exact same thing.
However, there are a few advantages to using the trailing return type form consistently (Phil Nash calls these "East End Functions", since the return type is on the east end).
Using parameters. Obviously when using parameters to determine the return type, you must use a trailing return type.
template <typename T>
auto print(T const& t) -> decltype(std::cout << t) { return std::cout << t; }
Name lookup. In a trailing return type, name lookup includes the class scope for member function definitions. This means you don't have to retype the class if you want to return a nested class:
Type C::foo() { ... } // error: don't know what Type is
C::Type C::foo() { ... } // ok
auto C::foo() -> Type { ... } // ok
Likewise, for defining member functions where the class name for some reason must be disambiguated to be in the global namespace and the return type is a class:
D ::C::foo() { ... } // error, parsed as D::C::foo() { ... }
auto ::C::foo() -> D { ... } // ok
If you are returning types like pointers to functions or pointers to arrays, it's actually possible to both write and understand the declaration using the trailing type syntax - whereas getting the syntax correct in the "normal" syntax is a challenge in its own right, understanding it even more so. These are both declarations of a function that takes no parameters and returns a pointer to function that takes an int
and returns an int
:
int (*get_function())(int); // traditional
auto get_function() -> int (*)(int); // trailing return type
A more reasonable ordering of information. Let's say you want to write a function to_string
that takes an int
and returns an string
. That's a pretty sensible way of phrasing that. Function name, parameters, return type. You wouldn't say you want to write a string
-returning function named to_string
that takes an int
. That's an awkward order of information. The name is the most important, followed by the parameters, followed by the return type. The trailing-return-type form allows you to order these pieces of information better.
Especially since the return type can be omitted (deduced). It just makes more sense to omit the last thing rather than the first thing. This is similar to why many newer languages have variable declaration syntax of the form let name: type
. The name
is most important, so it goes first, and the type
can be omitted, so it goes last.
There are cases where trailing-return-type is mandatory, there are cases where it is helpful, and there are cases where it does the same thing. There are not cases where it is worse for reasons other than simply character count.
Plus, mathemtically we're used to thinking of functions as A -> B
and not so much B(A)
, and so auto(*)(A) -> B
as a function pointer taking an A
and returning a B
is a little closer to that view than B(*)(A)
.
On the other hand, writing auto main() -> int
looks ridiculous. At least initially.
But honestly, that's mostly because of unfamiliarity. There's nothing inherently ridiculous about it. If anything, it's a bit unfortunate that the language uses auto
here as a way to declare a function - not because it's too long (i.e. some other languages use fun
or even fn
) - but because it's not really distinct from other uses of auto
. If it were func
instead, I think it would have been better (although it would not make any sense to change now).
Ultimately, this is purely opinion based. Just write code that works.