Author Topic: C++ Trailing Return Types  (Read 3792 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
C++ Trailing Return Types
« on: December 01, 2018, 03:42:26 AM »
Thought I'd leave a link here. I'm starting to see trailing return types more and more in C++ code, especially regarding templates:
arrow operator (->) in function heading

The example being:
Code: cpp [Select]

template <typename T, typename T1> auto compose(T a, T1 b) -> decltype(a + b) {
   return a+b;
}


The -> introduces the return type, and it does it after the argument list, so that identifiers in the argument list (a and b) are in scope, and so can be used in the decltype expression.

I originally saw trailing return types with lambdas, though it seems it can be used with regular functions too. Mainly, I see it used with template functions.



I recently saw this syntax used in combination with std::enable_if, which is typically used on the return value of a function to conditionally enable template expansion for only certain types.

The OP2Utility project makes use of enable_if (actually the related enable_if_t), and does so using the standard practice of putting it on the return type. Here's an example from OP2Utility:
Code: cpp [Select]

// Trivially copyable data types
template<typename T>
inline std::enable_if_t<std::is_trivially_copyable<T>::value> Read(T& object) {
ReadImplementation(&object, sizeof(object));
}


Here's what that same example would look like using a trailing return type:
(Note: There is no benefit to using the new syntax here, it's just an example of the new syntax):
Code: cpp [Select]

// Trivially copyable data types
template<typename T>
inline auto Read(T& object) -> std::enable_if_t<std::is_trivially_copyable<T>::value> {
ReadImplementation(&object, sizeof(object));
}


Note that enable_if_t<Type> (with the trailing "_t") is a shorthand alias for enable_if<Type>::value.



The reason the return type is wrapped with enable_if, is to make use of another language feature, SFINAE (Substitution Failure Is Not An Error), to get the job done. That's a whole other topic, but in short, if type substitution results in a case you want to support, then the enable_if expression evaluates to the desired type (the return type) of the method, otherwise the enable_if expression accesses a field/alias (::value) which doesn't exist (effectively an error in normal code), but since SFINAE, it's not actually an error, and so the compiler simply discards this template from consideration for this type, rather than halting compilation with an error.

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Re: C++ Trailing Return Types
« Reply #1 on: December 01, 2018, 02:11:46 PM »
Yep. I've gotten template deduction failure errors in certain situations (which I'm still not totally clear on) when declaring a function like "template <typename T> std::some_trait_t<T> Func(T&)" that go away when using the "auto Func(...) -> type" syntax, so I've just started using that everywhere I use type_traits or similar on the return type.
« Last Edit: December 01, 2018, 04:31:31 PM by Arklon »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: C++ Trailing Return Types
« Reply #2 on: December 01, 2018, 11:22:46 PM »
Fascinating, if you run across any examples, I've love to see them. I've found there's a lot to be learned from trying to do things the wrong way, and seeing what error messages you get.  ;D