auto
and decltype
Explained,
by Thomas Becker
about me
Taking our cue from
one of the examples of the previous section,
let us assume that we're working on some numerical computations
using floating point arithmetic, and we frequently need the min and
max of two numbers that may come in as different types,
such as int and double . This precludes the direct use of std::min ,
since the latter requires its arguments to be of the same type:
int i = 42; double d = 42.1; auto a = std::min(i, d); // error: ambiguous template parameterIf we wanted to use std::min , we would have to write
auto a = std::min(static_cast<double>(i), d);
This gets old pretty quickly, especially in a day and age where we
want even our
C++ code to look like Python. So rather naturally, we would want a
version of min and max functions is not a good idea.
Instead, we should aim for something that is meant to be used for our specific
purpose only. We probably already have
a bunch of utilities in a namespace called something like
floating_point_utilities . Now we want to put functions fpmin
and fpmax in there that allow us to write
using namespace floating_point_utilities; auto a = fpmin(i, d);
Since the new functions are in our own namespace, we could of course call them
The generic functions decltype ,
you may end up writing the following, and you would not
be the first one to do so:
<horrible> template<typename T, typename S> auto fpmin(T x, S y) -> decltype(x < y ? x : y) { return x < y ? x : y; } </horrible>According to our discussion in the previous section, the type decltype(x < y ? x : y)may or may not be a reference. If the types of T and S are the same,
then it is a reference. If they are a mixture
like int and double ,
it is not. In the former case,
our fpmin function as defined above
returns a reference to a local variable (a parameter
in this case). You will probably agree that returning
a reference to a local
variable or a temporary ranks high among the worst
and most embarrassing things a C++ programmer can do.
Depending on the circumstances, it may or may not be
caught via a compiler warning.
Here's the correct version of fpmin :
// Min function intended for basic numeric types. The arguments // may be of different type, in which case the one with lower // precision gets promoted. // template<typename T, typename S> auto fpmin(T x, S y) -> typename std::remove_reference<decltype(x < y ? x : y)>::type { return x < y ? x : y; }Now is a good time to tell you, as I promised earlier, why I am not a big fan of the use of the lexical token auto in
the trailing return type syntax, as seen above on our fpmin
function. As we know by now, the way that the other auto , the one
that is used when declaring and initializing a variable, deduces the
type of an expression is substantially different from the way
decltype works. In the context of trailing return
type syntax, only decltype matters.
Perhaps I'm overly sensitive, but the use of the lexical token
auto in this context leads my mind
astray, towards the reference-dropping semantics of the other
auto . That mental association is dangerous, as evidenced by the
fpmin example.
|