10

According to https://softwareengineering.stackexchange.com/a/180616/432039 suggested, I know the answer advised "auto" should be used instead of the actual type when declaring variables.

However, the question is about the readability of code before and after using auto, but I found the biggest problem of "auto" is not the readability, instead "auto" hinders me to search the code that I need to fix by using keyword like 'MyClass ' in variable declaration like "MyClass myclass = ...".

For example, suppose I have a c++ mobile game project that needs to load user settings using DTO "UserSettings", and UserSettings may be loaded from game server, mobile device, or edit by user at some pages, eg (ignore .h and class definition for simpler code):

OldUserLoginPage.cpp

OldUserLoginPage::loadUserData(){
    //some code call http
    UserSettings userSettings=loadUserDataResponse.userSettings;
    //some other code
}

UserResumePage.cpp

UserResumePage::viewDidLoaded(){
    //some init code
    UserSettings userSettings=AppData::getUserSettings();
    //some other init code
}

SettingsPage.cpp

 SettingsPage::onLoadDefaultButtonPressed(){
    //some code
    UserSettings userSettings=DefaultUserSettings::getInstance();
    //some other code
}

When the game works fine over few months, suddenly a use reports that the user settings seems failed to save, and I'm quite sure I would forget about which .cpp contain UserSettings, and also I would forget about loadUserDataResponse.userSettings, AppData::getUserSettings() and DefaultUserSettings::getInstance(), except I know I can search for keyword "UserSettings " to list all parts of codes that handle user settings.

However, if I just use "auto" instead of UserSettings, I can't search for keyword "UserSettings " to find which part of codes that use UserSettings. Although I may also able to find the related code by browsing each .cpp individually after using auto, search for keyword "UserSettings " helps me a lot faster to find and fix the bug in the code. Also when a new teammate enters the team and start maintaining the code, the teammate may be unable to know which actual page refers to ???Page.cpp immediately, but the new teammate can search for keyword "UserSettings " to find which part of the code in the project that may contain the bug.

Also even in a single .cpp, I find the type in variable declaration may also help me to find the related code quickly, for example, suppose there is a page about buying items and magic stones:

ShopPage.cpp

ShopPage::method1(){
   //some code
   this->updateTotalCost();
}

ShopPage::method2(){
   //some code
    this->updateTotalCost();
}
.
.
.
ShopPage::updateTotalCost(){
   //some other code
   double totalCost=item1.quanity*item1.price+item2.quanity*item2.price+...
}

Also after some day, suppose some users find the cost seems calculate wrongly, I forgot which function and variable name handles the cost calculating, except I remember the cost must be "double" type, then I can search for "double " to jump to the related code to investigate the problem.

While I agree "auto" may help me to write the code faster, code is being read and maintained more often than being written, so I would rather write c++ as if "auto" never exists, so that I can search for the code by variable declaration with specific type in order to find and fix the code quickly. So my question is, is the reason above the rationale to avoid using "auto"?

2
  • 4
    auto (implicit typing) is just yet another form of type erasure -- just like parametric polymorphism (templates), subtyping, function overloading, etc. It has nothing to do with the speed of writing code, it's about the use of abstraction and decoupling from dependencies. The same arguments which arise against auto are often also levelled against the use of abstract base classes, subtype polymorphism, template parameters and overloaded function names. (Typically covered by developer tooling and the use of unit testing. things like Navigation are usually handled by a decent IDE). Commented Jul 4 at 8:06
  • @BenCottrell your comment is much better than existing answers. Could you convert it, please?
    – Basilevs
    Commented Jul 6 at 8:12

4 Answers 4

9

It's certainly a valid reason.

Whether that's enough of a reason to avoid auto in your situation - that depends on whether there are superior reasons for using it. Presumably you can find such reasons in the design documents that led to introducing auto in the first place; here's a paper by Stroustrup et al. that introduces the concept.

(Personally, I'm a huge fan of type systems doing work that you'd otherwise have to do in your head. So to me, the ideal way of doing type inference would be simply to write "auto" and the IDE replacing it with the type that it has inferenced - which would combine the advantages of both worlds. But apparently not enough people think that to make it happen.)

3
  • 1
    +1 for the ideal way.
    – Kromster
    Commented Jul 4 at 10:37
  • 5
    Fixing the type when writing the code would negate one of the main benefits of auto: that it is determined at compile time and changes when inferred type changes instead of requiring the programmer to change every single instance.
    – ojs
    Commented Jul 4 at 11:31
  • 6
    If you're using an IDE anyways, you might as well use one that understands auto when searching by type usages. Then this entire problem disappears... Commented Jul 4 at 22:37
7

No, I don't think this is a valid reason to avoid all usage of auto in general. It may be a valid reason for certain cases (though both of your examples look somewhat debatable to me).

Let me first say I disagree partially with Herb Sutter's accepted answer to that other question, that you should use auto as default, as long as you don't want an explicit conversion. When you read the comments to that answer, you see I am not alone with my opinion, because that strategy can have a negative impact on readability.

The cases where I use auto are mainly the cases where

  • the type name would be unneccessarily repeated on the very same line

  • the exact type name would be something like a longish technical iterator type name and does not really matter

In the first case, usage of auto will not degrade the "global searchability", and in the second case, it is unlikely you will ever have to make a global search for the type name.

Let me comment on your examples:

  • your second example shows a case where a global search for the type name makes no sense - double is as unspecific as auto. Still, I guess also Herb Sutter would agree to use an explicit type name - in case you want to make sure totalCost has the type double, regardless of what types are used on the right-hand side. That's what Herb means by "beeing committed to a specific type".

  • in your first example, I don't think it is a good strategy to start with a global search for UserSettings when searching the root cause for a certain error. When a save operation "fails", I would first set a breakpoint into the save operation itself and check if the operation is executed (and fails), or if it is not executed at all. For the latter case, as the next step, I would find out from where I expect the save operation to be called and set breakpoints into all those calling points. A good IDE might show you all potential callers, without such an IDE, a global search for the name of the save operation would be more useful.

    Of course, when your save operation was simply named save, and you have 30 other classes which a member save in your codebase, as a last resort, one could make a global search for UserSettings and hope the returned number of places is smaller than the number of places with the word save in the codebase. But as you see, the global search for a class name is probably not always as useful as it looks like at a first glance.

6

It is true that code is being read and maintained more often than it is written.
But that's one more reason to use auto !

auto to accelerate change propagation

For the maintenance of code, auto has indeed often (not always) the advantage of facilitating abstraction and ease decoupling from the types. Take your example:

double totalCost=item1.quantity*item1.price+item2.quantity*item2.price+....

Imagine that you find out that finally double is not sufficiently precise for the price, and you'd need to go for long double to avoid unacceptable loss. You'd have to go carefully through your code, and for every double look if it's concerned by the change or not. With auto, you let the compiler automatically propagate that change and will have less risks of forgetting one occurence.

See online example here.

Of course, type inference has its limits, and sometimes the programmer really has to chose the right type (often with the need of explicit casting some operands already in the expression to really achieve the intended result - see counter example here). Make that the exception, not the rule.

auto to prevent accidental slicing

Type inference also avoids nasty slicing errors, for example when MySpecialisedClass subclass needs to be used instead of MyClass and one declaration was forgotten, e.g:

MySpecialisedClass xyz("some", more, arguments);
... // lots of code 
MyClass uvw = xyz; // still looks fine - but slicing occurs ! 

See online example here demonstrating the advantage of auto.

But what about reading ?

There are a lot of languages with no explicit types specified or type inference similar to auto:

  • JavaScript: In my experience, native experienced JS devs rarely complain about unreadable due to missing type information. The main concern is when accidentally a variable causes a runtime error because of a wrong type. So it's more about type safety than readability.
  • Python, which is strongly but dynamically typed: Python devs that I know, usually don't complain either about the lack of explicit type and type hinting aims at type safety rather than readability as explained in PEP484
  • Many other modern languages promote type inference. For example Rust or Swift, to name only a few. Swift for exemple encourages type inference with let (like const auto) and var (like auto).

Why would the absence of explicit type be more difficult to read if it's C++ then ? My position is that it's because of habits, habits still prevent people to embrace modern C++ and its benefits.

Why not giving a try? Modern IDEs and editors perfectly do the trick in getting used to it. Example with Visual Studio Code, when hovering over an auto declared identified:

enter image description here

11
  • 4
    "Python coder usually not complain that their code is difficult to read because the type is not shown explicitly." Yes they do, there's a reason MyPy exists.
    – JAD
    Commented Jul 5 at 5:55
  • 1
    @JAD MyPy exists for static type checking, which is already a given in statically type-checked languages like C++. It's not for human readability. The reason for adding types to code is simply so that tools have sufficient information to be able to enforce a set of rules/constraints at build time and to be able to provide more assistance within an IDE. Adding more redundant types which can be automatically inferred by a tool does not help anybody. Commented Jul 5 at 6:29
  • 2
    @BenCottrell ok, let me be more accurate: there's a reason type hints were retrofitted into the language. And at least part of that is for humans to read. It's not just for type checking, but also for documenting APIs.
    – JAD
    Commented Jul 5 at 7:43
  • @JAD indeed, PEP484 explains that the goals of type hinting is static analysis and refactoring, but that it is not intended to change the paradigm of the dynamic typed language peps.python.org/pep-0484 - Thos confirms Ben's statement
    – Christophe
    Commented Jul 5 at 19:38
  • @JAD But you could loon at other examples also. Nobody complains about JavaScript var which looks like auto. Except for type checking to avoid at compile time the runtime errors. Other lodern languages also encourege this parctice like Swift.
    – Christophe
    Commented Jul 5 at 19:41
5

It depends on how clever your development software is. Searching for all uses of MyClass as text is easy. But some IDEs have the ability to search for MyClass as a symbol as well. That could be by using a macro with an expansion containing “MyClass”, use of “super” in a subclass, or any use of “auto” which produces a MyClass, MyClass& and so on.

Now if you want a MyClass instance and nothing else, feel free to use “MyClass” and not “auto”. You do use auto if you don’t really care about the actual type. For example, if a function returns an iterator, that is often a complicated type, and you don’t really care about the type, so you use auto.

Another good reason not to use auto: You have a class MyClass with subclasses MySub1 .. MySub10 and functions returning instances of those subclasses. You write some code that should in principle work with any instance of MyClass but currently needs a MySub3. Using auto, you’d get a reference to a MySub3 and you might call a method that is not a MySub method - which you consider a bug. Declare the variable as a reference to MyClass and thus cannot happen. (A clever compiler will know that the actual instance is always a MySub3 instance and replace virtual calls with direct calls which can then be unlined, so you lose nothing in performance).

Not the answer you're looking for? Browse other questions tagged or ask your own question.