I've finally started to read up on c++11 and I fail to understand why trailing-return-types are required.
I came across the following example, which is used to highlight the problem:
template<class Lhs, class Rhs>
decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;}
The example is illegal, because decltype(lhs+rhs)
does not work, since the identifiers lhs
and rhs
are only valid after the parsing phase.
I guess my question is about the timing of decltype
type resolution. If I am not mistaken, the keyword decltype
is used to determine the type of an expression at compile-time.
I fail to see a downside to having decltype
perform type resolution after all parsing is completed (which would work fine for the above example). I believe this would have been a simpler way to solve the problem...
Instead, the C++11 standard provides trailing-return-types:
template<class Lhs, class Rhs>
auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}
I have no doubt that I am missing something, since I fail to see the other use of trailing-return-types. Where is the flaw in my reasoning?
The trailing-return-types seem like an overly complex solution to me since having decltype
type resolution after parsing the full function body would work just as well?
Use auto and decltype to declare a function template whose return type depends on the types of its template arguments. Or, use auto and decltype to declare a function template that wraps a call to another function, and then returns the return type of the wrapped function.
In C++14, you can just use auto as a return type.
I fail to see a downside to having decltype perform type resolution after all parsing is completed (which would work fine for the above example).
The downside is that it's not possible without fundamentally altering the basic foundations of the C++ parsing and processing model.
In order to do what you suggest, the compiler will have to see the decltype
syntax and do some basic lexical analysis of the contents of the syntax. Then, it goes on to parse more of the source file. At some later point (when?), it decides to go, "hey, that stuff I looked at before? I'm going to do all of the parsing work for them now."
As a general rule, C++ doesn't support looking ahead for the definition of symbols. The basic assumption of the C++ parsing framework is that, if the symbol is not declared before it is used, it is a compiler error.
Classes can get away with lookahead, but only with respect to their members. This is in part because its quite clear when an id-expression could be referring to a member variable (ie: if it's not referring to an already declared local or global variable in scope). That's not the case here, where we're not sure what exactly the id-expression could be referring to.
Furthermore, what you suggest creates ambiguities. What does this mean:
int lhs;
template<class Lhs, class Rhs>
decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs);
Is the decltype syntax refering to the global lhs
variable, or the local lhs
function parameter?
The way we do it now, there's a clear delineation between these two:
int lhs;
float rhs;
template<class Lhs, class Rhs>
decltype(lhs+rhs) adding_func1(const Lhs &lhs, const Rhs &rhs);
template<class Lhs, class Rhs>
auto adding_func2(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs);
adding_func1
refers to the global variables. adding_func2
refers to the function parameter.
So you can either radically break every C++ compiler on the face of the Earth. Or you can simply late-specify your return type.
Or you can take the C++14 approach and not bother to state it at all.
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