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 headingThe example being:
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:
// 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):
// 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.