There are a lot of *_v and *_t suffixes, like std::is_same_v, std::invoke_result_t, result_of_t and milions of other such functions.
Why do they exist at all? Is it beneficial in any context to expose implementation details like std::result_of::type or std::is_same::value? Ignoring standard compliance, should the _v _t versions always be preferred? Could the ::type ::value versions have never existed at all?
TL;DR - _t aliases can shorten metaprogramming code significantly due to omitting ::type and typename. _v variable templates were added later for symmetry with _t aliases and because they're just better in every way.
_v variable templatesHere's a quote from proposal paper that introduced _v variable templates into C++17:
Variable templates like is_same_v<T, U> are superior to nested constants like is_same<T, U>::value for several reasons:
Obviously, the former is 5 characters shorter.
Less obviously, the former eliminates "disconnected verbosity". When working with type traits, it's common to have to say things like enable_if_t<!is_same<X, decay_t>::value, Z>, where "is_same" and "::value" are separated by a potentially substantial amount of machinery.
Having to say "::value" isn't a feature, it's a workaround for a language limitation that has been solved. (Note that this proposal doesn't touch the struct templates, so metaprogrammers who want the is_same type instead of its nested constant are unaffected.)
Also, important points from the adoption paper:
Following the success of the
_talias templates for type traits, a matching set of_vvariable templates have been proposed. The language support for variable templates arrived too late to confidently make this change for C++14, but experience since has shown that such variable templates are more succinct, can clean up the text in a similar way that the_taliases have been widely adopted through the standard, and the author's experience using them in his own implementation of the standard type traits library is that code is much simpler when written using such variable templates directly, rather than turning a value into a type, then performing template manipulations on the type, before turning the type back into a value.
The impact on the standard is that many places that referencesome_trait<T>::valuewould instead usesome_trait_v<T>. The saving is not quite as great as in the case of alias templates, as there is no irksometypenameto remove. However, the consistecy of using_tand_vto refer to traits, and not using::somethingto extract meaning is compelling.
_t aliasesSimilar reasoning was provided in paper that introduced _t aliases in C++14, with the extra benefit of adding typename, which NathanOliver remarked in his answer. Quote from the paper:
Unfortunately, the above-described flexibility comes with a cost for the most common use cases. In a template context, C++ requires that each “metacall” to a metafunction bear syntactic overhead in the form of an introductory
typenamekeyword, as well as the suffixed::type:typename metafunction-name<metafunction-argument(s)>::typeEven relatively straightforward compositions can rather quickly become somewhat messy; deeper nesting is downright unwieldy:
template< class T > using reference_t = typename conditional<is_reference<T>::value, T, typename add_lvalue_reference<T>::type>::type;Worse, accidentally omitting the keyword can lead to diagnostics that are arcane to programmers who are inexpert in metaprogramming details.
In our experience, passing metafunctions (rather than metadata) constitutes a relatively small fraction of metafunction compositions. We find ourselves passing metafunction results far more frequently. We therefore propose to add a set of template aliases for the library’s
TransformationTraitsin order to reduce the programmer burden of expressing this far more common case. Note, in the following rewrite of the above example, the absence of anytypenamekeyword, as well as the absence of any::typesuffix, thus condensing the statement from 3 to 2 lines of code:template< class T > using reference_t = conditional_t< is_reference<T>::value, T, add_lvalue_reference_t<T> >;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With