Why are the following two templates incompatible and can't be overloaded?
#include <vector>
template<typename T>
auto f(T t) { return t.size(); }
template<typename T>
auto f(T t) { return t.foobar(); }
int main() {
f(std::vector<int>());
}
I would think they are (more or less) equivalent with the following which compiles fine (as we cannot do decltype auto(t.size())
I can't give an exact equivalent without some noise..).
template<typename T>
auto f(T t) -> decltype(t.size() /* plus some decay */) { return t.size(); }
template<typename T>
auto f(T t) -> decltype(t.foobar() /* plus some decay */) { return t.foobar(); }
Clang and GCC complain main.cpp:6:16: error: redefinition of 'f'
if I leave off the trailing return type, however.
(Note that I am not seeking for the place in the Standard which defines this behavior - which you may include in your answer too, if you wish - but for an explanation of why this behavior is desirable or status-quo).
The deduced return type can clearly not be part of the signature.
However, inferring an expression that determines the return type (and participates in SFINAE) from return
statements has some issues. Let's say we were to take the first return
statement's expression and paste it into some adjusted, virtual trailing-return-type:
What if the returned expression depends on local declarations? This isn't necessarily stopping us, but it snarls the rules tremendously. Don't forget that we can't use the names of the entities declared; This could potentially complex our trailing-return-type sky-high for potentially no benefit at all.
A popular use case of this feature are function templates returning lambdas. However, we can hardly make a lambda part of the signature - the complications that would arise have been elaborated on in great detail before. Mangling alone would require heroic efforts. Hence we'd have to exclude function templates using lambdas.
The signature of a declaration couldn't be determined if it wasn't a definition also, introducing a whole set of other problems. The easiest solution would be to disallow (non-defining) declarations of such function templates entirely, which is almost ridiculous.
Fortunately the author of N3386 strove to keep the rules (and implementation!) simple. I can't imagine how not having to write a trailing-return-type yourself in some corner cases warrants such meticulous rules.