When Should I Use C++14 Automatic Return Type Deduction

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

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

Warning with automatic return type deduction: why do we need decltype when return defines the type anyway?

What am I doing wrong?

Nothing. GCC 4.8 implements auto-deduced return types, but as an enabled-by-default C++1y feature. Compiling with -std=c++1y will remove that warning.

[Answer converted from this comment.]

Return type deduction in C++14

I believe this is a bug in GCC's implementation of its extension to C++14's auto. Here is a program that appears to be intended to work:

auto f(auto i) {
return "";
}

int main() {
const char *s = f(1);
return 0;
}

It doesn't work, it fails with "error: invalid conversion from ‘int’ to ‘const char*’" because GCC determines, for some reason, that the return type must be the same as the parameter type.

That same bug can make code that's meant to be rejected, such as what's in your question, compile without problems.

Of course, this bug doesn't affect conformance, because no valid C++14 program can use auto parameters outside of lambdas.

It happens to have been reported about a week ago to the GCC developers as bug #64969, as a result of another question on SO about it.

Why does auto return type deduction work with not fully defined types?

Your guess is correct. A deduced return type is not actually deduced until the signature of the function is needed. This means that it will be deduced in the context of the call to getCallOperator, at which point Foo is fully defined.

This is specified in 7.1.6.4p12:

Return type deduction for a function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand.

Return type deduction in C++14 in assignment

C++ ain't VBA: the thing on the left hand side of the assignment is not used to deduce the type of the right hand side.

So the compiler requires an explicit type for return_five(). You inform the compiler of the type by writing return_five<int>().

Is it always safe to use C++14's auto function type return deduction in place of std::common_type?

No, it's not always safe.

I assume your math functions do more than this, but here is an example where the result will be different.

template <class T, class U>
std::common_type_t<T, U> add(T t, U u) { return t + u; }

If you call this function with two chars the result will be a char. Would you auto deduce the return type, it would yield an int.

When can automatic return type apply?

Introduction

There are a few simple rules that states when the return-type of a function can be deduced from the function body, and with that when auto is applicable as return-type.

These rules are all stated in the Standard (n3797) [1], and each rule is listed in it's own section in the remaining parts of this post.



[1] in section 7.1.6.4, auto specifier [dcl.type.elab].


Is there anything that can't be deduced using auto as the return-type?

[dcl.type.elab]p1 If the deduction is for a return statement and the initializer is a braced-init-list (8.5.4), the program is ill-formed.

auto func () { return {1,2,3}; } // ill-formed



Which type will be deduced if a function has more than one return-statement?

[dcl.type.elab]p9 If a function with a declared return type that contains a placeholder type has multiple return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.

auto gunc_1 (bool val) { // (1), ill-formed
if (val) return 123;
else return 3.14f;
}

auto gunc_2 (bool val) { // (2), legal
if (val) return static_cast<float> (123);
else return 3.14f;
}

Note: (1) is ill-formed since all return-statements are not of the same type, whereas (2) is legal since the two return-statements yields the same type.



What happens if the function doesn't have a return-statement?

[dcl.type.elab]p10 If a function with a declared return type that uses a placeholder type has no return statements, the return type is deduced as though from a return statement with no operand at the closing brace of the function body.

auto hunc () { } // legal, return-type is `void`



Can I use the function, before the return-type has been deduced?

[dcl.type.elab]p11 If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed. Once a return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements.

auto junc (); // declaration

void foo () { &junc; } // (1), ill-formed

auto junc () { // definition
return 123;
}

void bar () { &junc; } // (2), legal

auto recursive (int x) {
if (--x) return x + recursive (x); // (3), ill-formed
else return 0;
}

Note: We cannot take the address of junc inside foo since doing that requires knowledge about what the complete type of junc is, something which isn't know until we have provided a definition where the return-type has been deduced. (2) is therefor legal, whereas (1) isn't.

Note: (3) is also ill-formed since we must know the return-type of recursive at this point, but it isn't known. Having the return-statements in the opposite order would, however, be valid. That way the compiler would know recursive to return int when it hits return x + recursive (x).

C++14: deduced (auto) return types from constexpr with ternary expressions

The resulting type from evaluating a ternary expression is the common type of its second and third arguments.

By having the compiler deduce the return type, you force it to evaluate both of these arguments to the ternary expression. This means that the recursion doesn't end even when the terminating condition is reached, because when a==1, to figure out the return type of fact(0) the compiler must continue evaluating further recursive calls to fact, and endless recursion ensues.

By stating the return type, fact(0) does not need to be evaluated when a==1, and the recursion is able to terminate.


As for the case with the two return statements, the relevant standard clause is —

(from N4296) §7.1.6.4/9 [dcl.spec.auto]

If a function with a declared return type that contains a placeholder type has multiple return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.

In your example, in the call to fact<int>(1), the return type deduced from the first return statement is int, so the return type of fact<int>(0) in the second return statement cannot be anything but int as well. This means the compiler does not need to evaluate the body of fact<int>(0) and the recursion can terminate.

Indeed, if you force evaluation of the call to fact in the second return statement as well, for instance by changing the first example so that T is a non-type template argument

template <unsigned T>
constexpr auto fact() {
if(T==1)
return 1;
return T*fact<T-1>();
}

clang does fail with the error

fatal error: recursive template instantiation exceeded maximum depth of 256

Live demo

Are local class rules aligned to c++14 return type deduction?

The rule for static local variables is plain and simple:

Dynamic initialization of a block-scope variable with static storage
duration (3.7.1) or thread storage duration (3.7.2) is performed the
first time control passes through its declaration; such a variable is
considered initialized upon the completion of its initialization.

And accessing an object's members before its construction is disallowed by [basic.life]/(7.1).



Related Topics



Leave a reply



Submit