East End Functions
There has been a recent stirring of attention, in the C++ community, for the practice of always placing the const modifier to the right of the thing it modifies. The practice has even been gifted a catchy name: East Const (which, I think, is what has stirred up the interest).
As purely a matter of style it's fascinating that it seems to have split the community so strongly! There are cases for and against, but both sides seem to revolve around the idea of "consistency". For the East Const believers the consistency is in the sense that you can always apply one, simple, rule about what const means and where it goes. For the West Consters the consistency is with the majority of existing code out there - as well as the Core Guidelines recommendation!
Personally I've been an East Const advocate for many years (although not by that name, of course) - and converted the entire Catch codebase over to East Const quite early on.
But there's another style choice that I've not seen discussed quite as much, but has a number of parallels.
As with East vs West Const this is purely a matter of style (it doesn't change what the compiler generates), and one of the arguments in favour is consistency across application (there are some cases where you must do it this way) - but the main argument against is also consistency - with most existing code. Sound familiar? But what is it?
The issue is about where to specify return types on function signatures.
For most of C++'s history the only choice has been to write the type before the name of the function (along with any modifiers).
But since C++11 we've been able to write the type at the end of the function signature (after a ->
- and the function must be prefixed with the keyword auto
).
auto someFunc( int i ) -> std::string;
// instead of
std::string someFunc( int i );
So why would you prefer this style? Well, first there's that consistency argument.
It's the only way to specify return types for lambdas.
You're also required to use trailing return types if the type is a decltype
that is dependent on the name of one of the function's arguments. Indeed, that's the motivating case for adding the syntax in the first place. e.g.:
template <typename Lhs, typename Rhs>
auto add( Lhs const& lhs, Rhs const& rhs ) -> decltype( lhs + rhs ) {
return lhs + rhs;
}
A Foolish Consistency?
Given those cases where it is required, using the same syntax in all other cases would seem to be more consistent.
I'm not sure the consistency argument is as strong here as it is with East Const - there was never much confusion over what the return applied to, after all. But I think it's worth keeping in mind.
The next reason for is consistency with other languages.
Many languages, especially functional programming languages, exclusively use the trailing syntax for return types. Quite a few, e.g.
Swift, use the same ->
syntax.
It's not a strong reason on its own, but combined with the internal consistency argument I think there's something there.
However, for me at least, the most compelling rationale is for readability. Why do I think it's more readable? There are actually two parts to this:
-
Function declarations tend to line up. Certain qualifiers might spoil this effect, although one approach might be to group similarly qualified functions (e.g. all
virtual
s) together. This makes glancing through the list of function names much easier. -
The name of the function is usually the most important thing when you're browsing the code. If you're more interested in the return type it's usually because you already know which function you're interested in. So making the name the first thing you read (after the
auto
introducer) seems fitting.
auto doesItBlend() -> bool;
auto whatsYourFavouriteNumber() -> int;
auto add( double a, double b ) -> double;
void setTheControls();
(note that many who prefer this form, including myself, tend to still put void
first)
For me the arguments for are compelling. The arguments against really boil down to the same argument against East Const - inconsistency with older code. As Jon Kalb deliberated on in A Foolish Consistency, this sort of thinking can hold us back.
I've been favouring this style for more than a couple of years now. In fact I tracked down a post to the ACCU mailing list (linked here, but I believe you have to be a subscriber to read it) where I talked about it - and made all the same points I'm making here. My opinion since then has not changed much. Other than feeling more confident that it's The Right Thing.
So I think it's time we gave it a catchy name. Unlike East Const it already has a name, "trailing return types". It's not especially galvanising, though. Given the parallels to East vs West Const - and the fact that it, also, relates to the thing in question being placed to the left or the right, I propose East End Functions (vs West End Functions).
What about the redundant auto keyword?
Think of auto
, here, as the "function introducer". In other languages it might be spelt fun
or func
. If it makes you feel better you could always:
#define func auto
... actually don't. The point is, in languages that introduce a function with func
, then have a trailing return type, nobody gives it a second thought.
auto
is the same number of characters as func
. It's a shame it's not quite as expressive - but that's the price of legacy.
It shouldn't mean we "can't have nice things".