C++11 Tidbits: Decltype (Part 2, trailing return type)

Posted by Paolo Carlini on Oracle Blogs See other posts from Oracle Blogs or by Paolo Carlini
Published on Wed, 6 Jun 2012 09:53:31 +0000 Indexed on 2012/06/06 10:45 UTC
Read the original article Hit count: 278

Filed under:

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.

© Oracle Blogs or respective owner

Related posts about /Oracle