Recently I was working a friend who wanted to make C++ more Haskell-y, and we wanted a function that's basically like this:
auto sum(auto a, auto b) { return a + b; }
Apparently I can't use auto as a parameter type, so I changed it to this:
template<class A, class B> auto sum(A a, B b) { return a + b; }
But that doesn't work either. What we eventually realized we need this:
template<class A, class B> auto sum(A a, B b) -> decltype(a + b) { return a + b; }
So my question is, what's the point? Isn't decltype
just repeating information, since the compiler can just look at the return statement?
I considered that maybe it's needed so we can just include a header file:
template<class A, class B> auto sum(A a, B b) -> decltype(a + b);
... but we can't use templates like that anyway.
The other thing I considered was that it might be easier for the compiler, but it seems like it would actually be harder.
Case 1: With decltype
decltype
statementCase 2: Without decltype
So with those things in mind, what's the point of the trailing return type with decltype
?
In C++14, you can just use auto as a return type.
Type inference or deduction refers to the automatic detection of the data type of an expression in a programming language. It is a feature present in some strongly statically typed languages. In C++, the auto keyword(added in C++ 11) is used for automatic type deduction.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With