In C++11 it's fine to declare a function like
auto times2(double num) -> double; // A
and define it like
double times2(double num) { // B
return num*2;
}
The pair A, B
can also be mixed the other way around.
C++14 introduces a third way
auto times2(double num) { // C
return num;
}
C
be mixed with A / B
in a declaration/definition pair ?Can C
stand alone as a signature (when the body of the function is not yet provided) ?
// waiting for the definition to provide info on return type
auto times2(double);
7.1.6.4p13
auto
specifier[dcl.spec.auto]
Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type.
The above quotation makes it ill-formed to mix the new with the old.
More specifically any such mixing introduces an ambiguating since the only difference in the two declarations is the return-type of the function; two declarations where one uses auto
and the other a non-deduced type T
are, in other words, not referring to the same function.
auto f ();
auto f () { return 123; } // legal, return type is `int`
auto g ();
auto g () -> int; // ill-formed, an invalid overload of `g` introduced
int h ();
auto h (); // ill-formed, an invalid overload of `h` introduced
There are rules related to automatic return-type deduction in C++14 that states that a function which yet hasn't had its return-type deduced is not usable in a context where such is required, that means that (A) below is not equivalent of an old-school declaration (ie. that a function can be used without yet having the definition).
auto foobar (double); // (A)
int main () {
auto ret = foobar(3.14f); // ill-formed, return type is not known (yet)
}
...
7.1.6.4p9
auto
specifier[dcl.spec.auto]
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. ...
auto
is... useless?No, far from it, there are cases where a forward declaration certainly is required and contexts where the return-type mustn't be known until a later time, such as inside a template.
template<class T>
auto foobar (T);
template<class T>
auto barbaz (T val) { return foobar (val); }
template<class T>
auto foobar (T val) { return val * 2; }
int main () {
barbaz (1234); // return-type is int
barbaz (3.1f); // return-tupe is float
}
Inside barbaz
cannot know the return-type of foobar
until we have actually instantiated barbaz
, but without a forward declaration of auto foobar(T)
, we couldn't refer to such name inside our template.