Legitimate uses of the trailing return type syntax as of C++14
Legitimate uses of the trailing return type syntax as of C++14
Is there actually any reason to use the following syntax anymore :
template<typename T>
auto access(T& t, int i)
-> decltype(t[i])
return t[i];
Now that we can use :
template<typename T>
decltype(auto) access(T& t, int i)
return t[i];
The trailing return type syntax now seems a little redundant?
You are not the only one, C++ compilers often like that as well... they get confused quite easily with early implementations of new standards features. (Or rather: compiler A trips up over code which compiler B understands, and vice versa...)
– user268396
Aug 31 at 22:49
2 Answers
2
Deduced return types are not SFINAE friendly. This overload will simply drop out of the overload set if t[i]
is invalid:
t[i]
template<typename T>
auto access(T& t, int i)
-> decltype(t[i])
return t[i];
Whereas this overload will not, leading to a hard error:
template<typename T>
decltype(auto) access(T& t, int i)
return t[i];
Demo
Also, you can run into issues with conflicting deduced return types. Consider if I wanted to return a std::optional<T>
. The following code doesn't compile since std::nullopt_t
is not the same type as std::optional<T>
:
std::optional<T>
std::nullopt_t
std::optional<T>
#include <optional> // C++17 standard library feature
template <typename T>
auto foo(T const& val)
if (val.is_invalid()) return std::nullopt;
return val.some_function_returning_an_optional();
Trailing return types let you specify exactly which expressions' type to return:
template <typename T>
auto foo(T const& val)
-> decltype(val.some_function_returning_an_optional())
if (val.is_invalid()) return std::nullopt;
return val.some_function_returning_an_optional();
You could use a leading return type, but it would require the use of std::declval
, which makes it harder to understand:
std::declval
template <typename T>
decltype(std::declval<T const&>().some_function_returning_an_optional())
foo(T const& val)
if (val.is_invalid()) return std::nullopt;
return val.some_function_returning_an_optional();
Demo
The second example isn't good because you can just do normal leading return type. I feel like expression sfinae is all that's left.
– Nir Friedman
Aug 31 at 23:30
@NirFriedman It's a simplified example, but I've encountered a case where it saves me. The return type depended on the arguments, but it was also an optional. It would be possible to use a leading return type, but not nice at all. I'll improve the second example
– Justin
Aug 31 at 23:34
Well, when we have concepts and use them thoroughly, return-type-deduction will have an upswing, as we get SFINAE through that instead. It will probably only need a few decades.
– Deduplicator
Sep 1 at 11:09
@Deduplicator It probably depends on the case. For ad-hoc constraints,
-> decltype(t[i])
is much shorter than requires requires(T const& v, int i) v[i];
. But if certain constraints are used enough, they'd probably be turned into named concepts, which will work just fine.– Justin
Sep 1 at 15:53
-> decltype(t[i])
requires requires(T const& v, int i) v[i];
Yes, at least three reasons:
T[i]
And there's also a fourth reason in Justin's answer.
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Maybe it is just me but I sometimes want to look at function definitions and immediately know what it is going to return without looking at the implementation
– Lakshay Garg
Aug 31 at 22:48