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

153 views Asked by At

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?

3

There are 3 answers

3
Silvio Mayolo On BEST ANSWER

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() {
  ...
}
0
273K On

The second option can sometimes be considered more preferable, it allows to implement shorter a class member function that return a type defined in a class scope. But this is not always true, depends on an outer class name.

#include <iostream>

class Outer {
 public:
  class Inner {};
  Inner m();
  Inner n();
};

auto Outer::m() -> Inner {  // The name scope Outer:: is known and can be omitted
  return {};
}
// vs
Outer::Inner Outer::n() {  // Outer is a short name and the entire definition is shorter
  return {};
}
0
Some programmer dude On

It seems both existing answers are missing one important case: When the return-type must be deduced, for example as a result of an expression.

The typical example is:

template<typename A, typename B>
auto add(A a, B b) -> decltype(a + b)
{
    // ...
}

Depending on the version of C++ you're using, and the complexity of the function, you must use trailing return type for this case.

Other than that, it could simplify some things as shown in the other answers. But beyond that it's still a personal and subjective choice.