c++autoreturn-type-deduction

Choosing between different function declaration styles in C++: auto vs. traditional return type syntax


There are two distinct styles for declaring functions, each employing a different approach to specifying the return type. The first adheres to the traditional syntax:

bool getMode() const;

On the other hand, the second leverages the auto keyword and the arrow syntax for the return type:

auto getMode() const -> bool;

My primary question is whether there are any substantial reasons to prefer one style over the other. I understand that readability is often subjective, but are there any specific scenarios, best practices, or performance considerations that could guide the choice between these two styles?


Solution

  • There is one semantic difference between the two, and it occurs when implementing a class method outside of the class. Suppose we have a class that defines a typedef and an instance method.

    class Example {
      using my_type_t = int;
      my_type_t foobar();
    };
    

    Now we go to our .cpp file to implement this method. If we use trailing return type syntax, then everything just works.

    auto Example::foobar() -> my_type_t {
      ...
    }
    

    However, if we put the type in front, then we have to prefix it with Example::

    Example::my_type_t Example::foobar() {
      ...
    }
    

    This is simply because of the compiler's parse order. The compiler only "figures out" that we're inside of the class Example once it sees Example::foobar. If the return type is trailing, then it occurs after the qualified name of the method, so we know the scope. If the return type occurs before we see that name, then the return type is evaluated in the surrounding scope and has to be fully qualified.

    This gets even messier with template classes. Compare

    // Trailing syntax
    template <typename T>
    auto Example<T>::foobar() -> my_type_t {
      ...
    }
    
    // Prefix syntax
    template <typename T>
    typename Example<T>::my_type_t Example<T>::foobar() {
      ...
    }