Since c++11 it has been possible that instead of declaring your function as “int name(arguments);” you can now do “auto name(arguments) -> int;”. The place I work at has it as style rule that all functions need to be declared that way. Now obviously this is not that large of a thing, and a consistent style is more important than my opinion here. But this has always felt like a weird thing, adding extra bloat to reading code. Anyways looking around I saw some positives to this construction, generally with the use of long return types, that are paramount when using templates. Here the benefit is that the function name is not hidden behind multiple template declarations, which does seem like a good argument. Also lamndbas generally use this. However I personally see some negatives here with using this foe every function, namely:
- extra bloat when typing/reading the code. This however could be automated to switch between the needed representations. It currently isent so I personally have ti type the auto and trailing return type manually, its not a lot, but still. Also reading code has become a bit more annoying if you have a lot of function overrides as you now have to first look which block of declarations have the smae function name and then parse which one has the correct return type.
- inconsistency with other typed programming languages: This one is probably why it irks me, but (and correct me on this) I dont know of a c style typed programming language that supports this type of syntax. Python has typehints(which you should use, please), which are declared after the function, and I remember Haskell also has their return type after the function name. But both of thede languages serve a different function than c++. More similar languages like c# and java dont support trailing return types.
Anyway enough of me ranting, I would like to know wath the other opinions here are. And whether this rant is missing se important arguments?
It is useful in very specific cases when using templates since C++ compiler processes from left to right and template types are not in scope before the function signature. Using auto keyword you can work around it specifying auto before the function signature and then later once everything is in scope, you specify the actual type via “->”.
Trailing return types are strictly superior since they allow the type to be written in terms of arguments, if necessary.
Deduction can sometimes take its place but often makes the code less obvious.
A minor side-effect is that it makes all function names line up, without introducing line breaks (which break
grep
). If only we could do the same for variables …TypeScript also has the type after the method/function, I think to make it easier to re-use and port existing JavaScript code.
I prefer to put return types on their own line:
MyReturnType doTheThing(int arg1, float arg2);
This keeps function names aligned vertically, which I find helps with readability. It also gives long return types an entire line to work with.
While I do like using
auto
inside function definitions, I don’t like it for return types because it hurts readability. Having the explicit type available to the reader reduces cognitive load. I use trailing return types on non-trivial lambda functions for this same reason.I don’t use it myself, but I don’t have a strong opinion on that. My beef if with deduced return types, especially with templates. It is typical in modern C++ code to see such function:
template<typename ~whatever~> auto foo(~some arguments which types are deduced from nested templated conditional using declarations that you don't understand~) { return ~call to other template function that's 10 levels deep and is also conditional on properties of template parameters with some ifdefs for fun~; }
That makes it absolutely impossible to figure out what types function takes as parameters and what type it returns until you hit 10-page compiler error that will explain everything (which is also will force you to read through implementation details of this function to figure out how to fix it). And often IDEs can’t help you either.
But what about something like this?
template<typename T> auto make_foo(T&& foo_arg) { return foo<std::decay_t<T>>{std::forward<T>(foo_arg)}; }
This is exactly the pathological case you gave as example, but I find this extremely readable. Specifying the return type would actually not help at all and the pattern the easily recognizable in the end.
If your naming is bad, arguments not clear, and implementation not recognizable and hard to infer what it’s doing then that’s a totally different problem and I would say trailing return type and deduced return type is the least of my concerns.
The type annotations in Python are the same: def functionname() -> ReturnType: …
I use trailing return type exclusively. It just makes the code more readable. Compactness is almost unaffected and readability is more important anyway. Trailing return type is also more compact in many normal cases non temaplate case so I think that argument is moot.
The name of the function is the first thing you want to read. The most important thing once you found your function is then it’s parameters. If you found the right overload, you know the parameters and what they mean then you want to know what it returns.
Trailing return type just have better ergonomics for the reader and also align the functions as a bonus. It so make name resolution better too.
I have yet to take a liking to auto.
I have found it nice to use for large types (nested containers, lambdas) which are only used once, and I would not necessarily want a typedef. However I also dont like using it too much its basically trading up coding speed for reading speed. And tile and time again it has been found that the latter one is done a lot more.
You need to have a look at expression templates then. You won’t survive without auto (or a concept).
Such programming ginmicks is nothing new. Thankfully, when it comes to what I use C++ for, auto would in most cases just be a result of bad programming. It will be interesting to see where C++ goes with this.
I wouldn’t say expression templates are a “gimmick”. You likely never wrote high-performance numerical programs in C++ which is why you might think so, but they are essential once you start dealing with matrices and want to avoid unnecessary read/writes from/to the RAM, and it would be actually “bad programming” to not use expression templates to fuse computational kernels at compile-time.
You’re right. My hpc experience so far has been mostly older fortran or C but particularly fortran. Performance hasn’t been an issue in my current C++ codes as for the scale I need them for they run in a matter of seconds which is more than fine in numerical programming.
So I suppose auto fits in well with those templates?