Why can't we have automatically deduced return types?
Solution 1:
Well - time passed since the original question was asked and the answer now is that you can!
Yes, it is true that the question is tagged C++11 - with which you still cannot do what the OP is asking for. But it's worthwhile to show what is doable with C++14 and later.
Since C++14 this is valid:
template<class A, class B>
auto sum(A a, B b) {
return a + b;
}
And since C++20 this is also valid:
auto sum(auto a, auto b) {
return a + b;
}
The following is the C++11 answer, kept here for historical reasons, with some comments from the future (C++14 and later):
What if we have the following:
template<class A, class B, class C>
auto sum(A a, B b, C c) {
if (rand () == 0) return a + b;
// do something else...
return a + c;
}
.. where a + b
and a + c
expressions yield different type of results.
What should compiler decide to put as a return type for that function and why?
This case is already covered by C++11 lambdas which allow to omit the return type as long as return
statements can be deduced to the same type (NB standard quote needed, some sources claim only one return expression is allowed and that this is a gcc glitch).
A note from the future (C++14 and on): the example above is still not valid, you may only have a single possible return type. However if there are different return types but the actual return type can be deduced at compile type, then we have two different functions, which is valid. The following for example is valid since C++17:
template<class A, class B, class C>
auto sum(A a, B b, C c) {
if constexpr(std::is_same_v<A, B>) return a + b;
else return a + c;
}
int main() {
auto a1 = sum(1, 2l, 3.5); // 4.5
auto a2 = sum(1, 2, 3.5); // 3
}
Back to the original C++11 answer, explaining why the requested syntax is not supported:
A technical reason is that C++ allows the definition and declaration to be separate.
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
The definition of the template could be in the header. Or it could be in another file, so that you don't have to wade through pages and pages of function definitions when looking through an interface.
C++ has to account for all possibilities. Restricting trailing return types to just function definitions means that you can't do something as simple as this:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
A note from the future (C++14 and on): you still cannot have a declaration with auto
return type, if the definition is not available when the compiler sees the call.
Solution 2:
but we can't use templates like that anyway.
First, trailing return types aren't purely a template thing. They work for all functions. Secondly, says who? This is perfectly legal code:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
The definition of the template could be in the header. Or it could be in another file, so that you don't have to wade through pages and pages of function definitions when looking through an interface.
C++ has to account for all possibilities. Restricting trailing return types to just function definitions means that you can't do something as simple as this:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
And this is fairly common for many programmers. There's nothing wrong with wanting to code this way.
The only reason lambdas get away without the return type is because they have to have a function body defined with the definition. If you restricted trailing return types to only those functions where the definition was available, you wouldn't be able to use either of the above cases.
Solution 3:
There is no technical reason why it is not possible. The main reason they haven't is because the C++ language moves very slowly and it takes a very long time for features to be added.
You can nearly get the nice syntax you want with lambdas (but you can't have templacised arguments in lambdas, again for no good reason).
auto foo = [](int a, double b)
{
return a + b;
};
Yes, there are some cases where the return type could not be automatically deduced. Just like in a lambda, it could simply be a requirement to declare the return type yourself in those ambiguous cases.
At the moment, the restrictions are just so arbitrary as to be quite frustrating. Also see the removal of concepts for added frustration.