When should I use C++14 automatic return type deduction?

chris picture chris · Apr 1, 2013 · Viewed 49k times · Source

With GCC 4.8.0 released, we have a compiler that supports automatic return type deduction, part of C++14. With -std=c++1y, I can do this:

auto foo() { //deduced to be int
    return 5;
}

My question is: When should I use this feature? When is it necessary and when does it make code cleaner?

Scenario 1

The first scenario I can think of is whenever possible. Every function that can be written this way should be. The problem with this is that it might not always make the code more readable.

Scenario 2

The next scenario is to avoid more complex return types. As a very light example:

template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
    return t + u;
}

I don't believe that would ever really be a problem, though I guess having the return type explicitly depend on the parameters could be clearer in some cases.

Scenario 3

Next, to prevent redundancy:

auto foo() {
    std::vector<std::map<std::pair<int, double>, int>> ret;
    //fill ret in with stuff
    return ret;
}

In C++11, we can sometimes just return {5, 6, 7}; in place of a vector, but that doesn't always work out and we need to specify the type in both the function header and the function body. This is purely redundant, and automatic return type deduction saves us from that redundancy.

Scenario 4

Finally, it can be used in place of very simple functions:

auto position() {
    return pos_;
}

auto area() {
    return length_ * width_;
}

Sometimes, though, we might look at the function, wanting to know the exact type, and if it isn't provided there, we have to go to another point in the code, like where pos_ is declared.

Conclusion

With those scenarios laid out, which of them actually prove to be a situation where this feature is useful in making the code cleaner? What about scenarios I have neglected to mention here? What precautions should I take before using this feature so that it doesn't bite me later? Is there anything new this feature brings to the table that isn't possible without it?

Note that the multiple questions are meant to be an aid in finding perspectives from which to answer this.

Answer

Steve Jessop picture Steve Jessop · Apr 1, 2013

C++11 raises similar questions: when to use return type deduction in lambdas, and when to use auto variables.

The traditional answer to the question in C and C++03 has been "across statement boundaries we make types explicit, within expressions they are usually implicit but we can make them explicit with casts". C++11 and C++1y introduce type deduction tools so that you can leave out the type in new places.

Sorry, but you're not going to solve this up front by making general rules. You need to look at particular code, and decide for yourself whether or not it aids readability to specify types all over the place: is it better for your code to say, "the type of this thing is X", or is it better for your code to say, "the type of this thing is irrelevant to understanding this part of the code: the compiler needs to know and we could probably work it out but we don't need to say it here"?

Since "readability" is not objectively defined[*], and furthermore it varies by reader, you have a responsibility as the author/editor of a piece of code that cannot be wholly satisfied by a style guide. Even to the extent that a style guide does specify norms, different people will prefer different norms and will tend to find anything unfamiliar to be "less readable". So the readability of a particular proposed style rule can often only be judged in the context of the other style rules in place.

All of your scenarios (even the first) will find use for somebody's coding style. Personally I find the second to be the most compelling use case, but even so I anticipate that it will depend on your documentation tools. It's not very helpful to see documented that the return type of a function template is auto, whereas seeing it documented as decltype(t+u) creates a published interface you can (hopefully) rely on.

[*] Occasionally someone tries to make some objective measurements. To the small extent that anyone ever comes up with any statistically significant and generally-applicable results, they are completely ignored by working programmers, in favour of the author's instincts of what is "readable".