C++11 Tidbits: Decltype (Part 2, trailing return type)
- by Paolo Carlini
Following on from last tidbit showing how the decltype
operator essentially queries the type of an expression, the second
part of this overview discusses how decltype can be syntactically
combined with auto (itself the subject of the March 2010 tidbit).
This combination can be used to specify trailing return types, also
known informally as "late specified return types".
Leaving aside the technical jargon, a simple example from section
8.3.5 of the C++11 standard usefully introduces this month's
topic. Let's consider a template function like:
template <class T, class U>
???
foo(T t, U u)
{
return t + u;
}
The question is: what should replace the question marks?
The problem is that we are dealing with a template, thus we don't
know at the outset the types of T and U. Even if
they were restricted to be arithmetic builtin types, non-trivial rules
in C++ relate the type of the sum to the types of T and
U. In the past - in the GNU C++ runtime library too -
programmers used to address these situations by way of rather ugly
tricks involving __typeof__ which now, with
decltype, could be rewritten as:
template <class T, class U>
decltype((*(T*)0) + (*(U*)0))
foo(T t, U u)
{
return t + u;
}
Of course the latter is guaranteed to work only for builtin
arithmetic types, eg, '0' must make sense. In short: it's a hack. On
the other hand, in C++11 you can use auto:
template <class T, class U>
auto
foo(T t, U u) -> decltype(t + u)
{
return t + u;
}
This is much better. It's generic and a construct fully supported by
the language.
Finally, let's see a real-life example directly taken from the C++11
runtime library as implemented in GCC:
template<typename _IteratorL, typename _IteratorR>
inline auto
operator-(const reverse_iterator<_IteratorL>& __x,
const reverse_iterator<_IteratorR>& __y)
-> decltype(__y.base() - __x.base())
{ return __y.base() - __x.base(); }
By now it should appear be completely straightforward.
The availability of trailing return types in C++11 allowed fixing a
real bug in the C++98 implementation of this operator (and many
similar ones). In GCC, C++98 mode, this operator is:
template<typename _IteratorL, typename _IteratorR>
inline typename reverse_iterator<_IteratorL>::difference_type
operator-(const reverse_iterator<_IteratorL>& __x,
const reverse_iterator<_IteratorR>& __y)
{ return __y.base() - __x.base(); }
This was guaranteed to work well with heterogeneous
reverse_iterator types only if difference_type was
the same for both types.