this post was submitted on 15 Jul 2023
11 points (100.0% liked)

C++

1763 readers
1 users here now

The center for all discussion and news regarding C++.

Rules

founded 1 year ago
MODERATORS
 

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?

top 14 comments
sorted by: hot top controversial new old
[–] [email protected] 6 points 1 year ago

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 "->".

[–] [email protected] 3 points 1 year ago

TypeScript also has the type after the method/function, I think to make it easier to re-use and port existing JavaScript code.

[–] [email protected] 3 points 1 year ago* (last edited 1 year ago)

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.

[–] [email protected] 3 points 1 year ago

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 ...

[–] [email protected] 2 points 1 year ago (1 children)

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.

[–] [email protected] 1 points 1 year ago

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.

[–] [email protected] 1 points 1 year ago

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.

[–] [email protected] 1 points 1 year ago

The type annotations in Python are the same: def functionname() -> ReturnType: ...

[–] [email protected] 0 points 1 year ago (2 children)

I have yet to take a liking to auto.

[–] [email protected] 2 points 1 year ago

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.

[–] [email protected] -1 points 1 year ago (1 children)

You need to have a look at expression templates then. You won't survive without auto (or a concept).

[–] [email protected] 1 points 1 year ago (1 children)

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.

[–] [email protected] -2 points 1 year ago* (last edited 1 year ago) (1 children)

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.

[–] [email protected] 2 points 1 year ago* (last edited 1 year ago)

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?

load more comments
view more: next ›