Take you to Template Programming Foundation 2

Posted by kender on Mon, 07 Feb 2022 18:26:48 +0100

Preface

Looking at the list of template variables I've shared before, template type derivation, template basics in template forward and move, you've learned enough about template basics, and now you're all set to bring them into real life. Explain train_at the beginning Before the type class, we still need to look at the underlying wheels, or we'll just say train_ There are a lot of things you don't know about type.

1. Source Code

The old rule is to paste out the source code first. I'll parse everything that comes up here next.

template <class _Ty>
struct remove_const { // remove top-level const qualifier
    using type = _Ty;
};

template <class _Ty>
struct remove_const<const _Ty> {
    using type = _Ty;
};

template <class _Ty>
using remove_const_t = typename remove_const<_Ty>::type;

template <class _Ty>
struct remove_volatile { // remove top-level volatile qualifier
    using type = _Ty;
};

template <class _Ty>
struct remove_volatile<volatile _Ty> {
    using type = _Ty;
};

template <class _Ty>
using remove_volatile_t = typename remove_volatile<_Ty>::type;

template <class _Ty>
struct remove_cv { // remove top-level const and volatile qualifiers
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = _Fn<_Ty>; // apply cv-qualifiers from the class template argument to _Fn<_Ty>
};

template <class _Ty>
struct remove_cv<const _Ty> {
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = const _Fn<_Ty>;
};

template <class _Ty>
struct remove_cv<volatile _Ty> {
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = volatile _Fn<_Ty>;
};

template <class _Ty>
struct remove_cv<const volatile _Ty> {
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = const volatile _Fn<_Ty>;
};

template <class _Ty>
using remove_cv_t = typename remove_cv<_Ty>::type;

template <bool _First_value, class _First, class... _Rest>
struct _Disjunction { // handle true trait or last trait
    using type = _First;
};

template <class _False, class _Next, class... _Rest>
struct _Disjunction<false, _False, _Next, _Rest...> { // first trait is false, try the next trait
    using type = typename _Disjunction<_Next::value, _Next, _Rest...>::type;
};

template <class... _Traits>
struct disjunction : false_type {}; // If _Traits is empty, false_type

template <class _First, class... _Rest>
struct disjunction<_First, _Rest...> : _Disjunction<_First::value, _First, _Rest...>::type {
    // the first true trait in _Traits, or the last trait if none are true
};

template <class... _Traits>
_INLINE_VAR constexpr bool disjunction_v = disjunction<_Traits...>::value;

template <class _Ty, class... _Types>
_INLINE_VAR constexpr bool _Is_any_of_v = // true if and only if _Ty is in _Types
    disjunction_v<is_same<_Ty, _Types>...>;

#if _HAS_CXX20
_NODISCARD constexpr bool is_constant_evaluated() noexcept {
    return __builtin_is_constant_evaluated();
}
#endif // _HAS_CXX20

template <class _Ty>
_INLINE_VAR constexpr bool is_integral_v = _Is_any_of_v<remove_cv_t<_Ty>, bool, char, signed char, unsigned char,
    wchar_t,
#ifdef __cpp_char8_t
    char8_t,
#endif // __cpp_char8_t
    char16_t, char32_t, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>;

template <class _Ty>
struct is_integral : bool_constant<is_integral_v<_Ty>> {};

template <class _Ty>
_INLINE_VAR constexpr bool is_floating_point_v = _Is_any_of_v<remove_cv_t<_Ty>, float, double, long double>;

template <class _Ty>
struct is_floating_point : bool_constant<is_floating_point_v<_Ty>> {};

template <class _Ty>
_INLINE_VAR constexpr bool is_arithmetic_v = // determine whether _Ty is an arithmetic type
    is_integral_v<_Ty> || is_floating_point_v<_Ty>;

template <class _Ty>
struct is_arithmetic : bool_constant<is_arithmetic_v<_Ty>> {};

template <class _Ty>
struct remove_reference {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty;
};

template <class _Ty>
struct remove_reference<_Ty&> {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty&;
};

template <class _Ty>
struct remove_reference<_Ty&&> {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty&&;
};

template <class _Ty>
using remove_reference_t = typename remove_reference<_Ty>::type;

template <class _Ty>
using _Const_thru_ref = typename remove_reference<_Ty>::_Const_thru_ref_type;

template <class _Ty>
using _Remove_cvref_t = remove_cv_t<remove_reference_t<_Ty>>;

#if _HAS_CXX20
template <class _Ty>
using remove_cvref_t = _Remove_cvref_t<_Ty>;

template <class _Ty>
struct remove_cvref {
    using type = remove_cvref_t<_Ty>;
};
#endif // _HAS_CXX20

_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _XTR1COMMON_

2. Source Code Resolution

1. remove_const: Remove the const flag

template <class _Ty>
struct remove_const { // remove top-level const qualifier
    using type = _Ty;
};

template <class _Ty>
struct remove_const<const _Ty> {
    using type = _Ty;
};
//Using partial specialization to remove const, rules have been introduced before. When the best fit is found at the template level, you will see that the specialization can be satisfied and the world can use special edition. This feature is formally utilized to remove consts.
template <class _Ty>
using remove_const_t = typename remove_const<_Ty>::type;
//Define a new type.
//Say another thing: Template functions and template classes are used in different ways. Template functions can automatically deduce the original type by using parameters, but the type of template classes can not be deduced by parameters, they all need to be manually indicated by themselves. That's what I've been able to do with this source code, and he's designed a number of ways to make an indeterminate type known.


2. remove_volatile: remove volatile

(Feels like you haven't used a keyword, just knows not to let the compiler optimize it), like const, which uses specialization to extract keywords and redefine types

template <class _Ty>
struct remove_volatile { // remove top-level volatile qualifier
    using type = _Ty;
};
//Define a class template, and the extracted type is handed over to specialization. It does the same thing as recursive final output. There must always be a result, or it will recursively be an infinite loop, where the error is reported directly.
template <class _Ty>
struct remove_volatile<volatile _Ty> {
    using type = _Ty;
};
//The type after volatile removal is redefined by extracting volatile with specialization.
template <class _Ty>
using remove_volatile_t = typename remove_volatile<_Ty>::type;
//Redefine a type, the type after volatile.

3. remove_cv: remove const and volatile attributes

template <class _Ty>
struct remove_cv { // remove top-level const and volatile qualifiers
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = _Fn<_Ty>; // apply cv-qualifiers from the class template argument to _Fn<_Ty>
};
//The old rule defines the base class representation, giving all results a home. (Recursive results are similar, and one is always achieved.) With attribute specialization, template specialization is better than common templates in selecting and removing attributes.
template <class _Ty>
struct remove_cv<const _Ty> {
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = const _Fn<_Ty>;
};
//Specify const, remove const attribute. Const is called only if the keyword is used
template <class _Ty>
struct remove_cv<volatile _Ty> {
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = volatile _Fn<_Ty>;
};
//Specify volatile, remove specialization volatile. Special volatile removal is invoked only if the keyword is removed
template <class _Ty>
struct remove_cv<const volatile _Ty> {
    using type = _Ty;

    template <template <class> class _Fn>
    using _Apply = const volatile _Fn<_Ty>;
};
//Specify const volatile, remove specialization const volatile. Removing the specialized const volatile will only be invoked if the keyword is removed
template <class _Ty>
using remove_cv_t = typename remove_cv<_Ty>::type;
//Redefine the type after attributes are removed.

Note: Template template parameter usage and why type requires typename every time before link

4. _Disjunction: (Feels like the description is not clear)

  1. Type is bool_constant (true_type and false_type). Keep this in mind and I'll introduce you. (It's not my guess, there are subsequent calls to the class, which is just part of the real step of implementation, and when you look closely, he doesn't handle the parameters.)
  2. The purpose of the design is to save the value. When there is true_ When type appears, the type of the class is equal to true_type, none equals false_type
template <class _Ty, _Ty _Val>
struct integral_constant {
    static constexpr _Ty value = _Val;

    using value_type = _Ty;
    using type       = integral_constant;

    constexpr operator value_type() const noexcept {
        return value;
    }

    _NODISCARD constexpr value_type operator()() const noexcept {
        return value;
    }
};
//Define itself, values, and types as three types. type,value_type and value
//Fear people forget to extract base classes
template <bool _Val>
using bool_constant = integral_constant<bool, _Val>;
//Specialization of an extract base class is invoked only if the type is bool
using true_type  = bool_constant<true>;
using false_type = bool_constant<false>;
//Call the template above to define two new types. bool type with values of true and false


template <bool _First_value, class _First, class... _Rest>
struct _Disjunction { // handle true trait or last trait
    using type = _First;
};
//Define a base class whose purpose is 1. When the first parameter is true 2. For final processing, it is called when there are only two parameters. Anything will be saved.
template <class _False, class _Next, class... _Rest>
struct _Disjunction<false, _False, _Next, _Rest...> { // first trait is false, try the next trait
    using type = typename _Disjunction<_Next::value, _Next, _Rest...>::type;
};
//Specifying the base class above will only be invoked if the first parameter is false.
//And recursively calls themselves to handle variable parameters. Next::value gets the current value true or false.
//The last time because only two parameters <false, First> did not match the specialization class <false, False, Next>, so the base class was called.

Note: Understanding the usage of variable parameters link

5. disjunction: for _ Disjunction filters irregular data and extracts variable parameters using inheritance.

The purpose is to: Disjunction encapsulates another layer. But why is it covered with another layer? I guess it's probably because of the parameter, disjunction parameter is (T)_ Disjunction parameter (T::value,T), the parameter passed in from the outside is T, so you want to make _ Disjunction direct processing apparently makes no sense to suggest another layer.

template <class... _Traits>
struct disjunction : false_type {}; // If _Traits is empty, false_type
//Template base class. Filter for parameter 0, inherit false_type, stating that if the last value of parameter 0 is false, the _ Disjunction base class will not have compilation errors.
//This just adds _ The Disjunction parameter cannot be a zero limit. And inherits false_type, so that the outside world does not need to be aware when using it. Because type also inherits false_type or true_type, that is, the last type that can be returned regardless of right or wrong.
template <class _First, class... _Rest>
struct disjunction<_First, _Rest...> : _Disjunction<_First::value, _First, _Rest...>::type {
    // the first true trait in _Traits, or the last trait if none are true
};
//A parameter is called when it is greater than or equal to 1. Inheritance extracts variable parameters. To use _ Disjunction, returns false_type or true_type
template <class... _Traits>
_INLINE_VAR constexpr bool disjunction_v = disjunction<_Traits...>::value;
//Get the final value, false or true,

6. _Is_any_of_v: Determine if the unknown parameter exists in the list of known parameters

Enter an unknown type parameter, and several known parameters, and he will determine if the unknown parameter exists in the list of known parameters. Returns true in and false no longer. Will be_ Same passed in disjunction_as a parameter V

template <class, class>
_INLINE_VAR constexpr bool is_same_v = false; // determine whether arguments are the same type
//The compiler deduces that there are two types and sets them incorrectly.
template <class _Ty>
_INLINE_VAR constexpr bool is_same_v<_Ty, _Ty> = true;
//The last specialized version was called when the two types were the same. Variables are specialized
//The compiler deduces that a type is set correctly
template <class _Ty1, class _Ty2>
struct is_same : bool_constant<is_same_v<_Ty1, _Ty2>> {};
//Returns true when t1 and t2 type calls are made because specialization matching takes precedence over normal templates


template <class _Ty, class... _Types>
_INLINE_VAR constexpr bool _Is_any_of_v = // true if and only if _Ty is in _Types
    disjunction_v<is_same<_Ty, _Types>...>;
//See if the type of input exists in the list of known parameters.
//Disjunction_ V<is_ Same<_ Ty, _ Types>...>; What was actually passed in was
//disjunction_v<is_same<_Ty, T1>, is_same<_Ty, T2>,,,is_same<_Ty, Tn>>
//This is the use of variable parameter lists.... Is to repeat the previous use. It will automatically extract and call the way you specified it to execute.

Test demo

#include <iostream>
template <class _Ty, _Ty _Val>
struct integral_constant {
    static constexpr _Ty value = _Val;

    using value_type = _Ty;
    using type = integral_constant;

    constexpr operator value_type() const noexcept {
        return value;
    }

    _NODISCARD constexpr value_type operator()() const noexcept {
        return value;
    }
};
template <bool _Val>
using bool_constant = integral_constant<bool, _Val>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;

template <bool _First_value, class _First, class... _Rest>
struct _Disjunction { // handle true trait or last trait
    using type = _First;
};

template <class _False, class _Next, class... _Rest>
struct _Disjunction<false, _False, _Next, _Rest...> { // first trait is false, try the next trait
    using type = typename _Disjunction<_Next::value, _Next, _Rest...>::type;
};

template <class... _Traits>
struct disjunction : false_type {}; // If _Traits is empty, false_type

template <class _First, class... _Rest>
struct disjunction<_First, _Rest...> : _Disjunction<_First::value, _First, _Rest...>::type {
    // the first true trait in _Traits, or the last trait if none are true
};

template <class, class>
_INLINE_VAR constexpr bool is_same_v = false; // determine whether arguments are the same type
template <class _Ty>
_INLINE_VAR constexpr bool is_same_v<_Ty, _Ty> = true;

template <class _Ty1, class _Ty2>
struct is_same : bool_constant<is_same_v<_Ty1, _Ty2>> {};

template <class... _Traits>
_INLINE_VAR constexpr bool disjunction_v = disjunction<_Traits...>::value;

template <class _Ty, class... _Types>
_INLINE_VAR constexpr bool _Is_any_of_v = // true if and only if _Ty is in _Types
disjunction_v<is_same<_Ty, _Types>...>;

int main(void)
{
    bool a = _Is_any_of_v<int, bool, float>;
    return 0;
}

7. is_integral: Determines whether the parameter is compatible with integer types

template <class _Ty>
_INLINE_VAR constexpr bool is_integral_v = _Is_any_of_v<remove_cv_t<_Ty>, bool, char, signed char, unsigned char,
    wchar_t,
#ifdef __cpp_char8_t
    char8_t,
#endif // __cpp_char8_t
    char16_t, char32_t, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>;
//It's a simple call, remove_ Cv_ T<_ Ty removes the const and volatile attributes of the input type and then inputs all integer compatible types as other parameters using _ Is_any_of_v Compare.
template <class _Ty>
struct is_integral : bool_constant<is_integral_v<_Ty>> {};
//Is_ Integral_ V<_ Ty>returns false or true at the end
//And finally bool_ Constant<false>or bool_ Constant<true>
//is_integral::value false or true;

8. is_floating_point: Determines if it is a floating point type

template <class _Ty>
_INLINE_VAR constexpr bool is_floating_point_v = _Is_any_of_v<remove_cv_t<_Ty>, float, double, long double>;

template <class _Ty>
struct is_floating_point : bool_constant<is_floating_point_v<_Ty>> {};
//Identity determines whether or not it is a reshaping type

9. is_arithmetic: Determine if it is a number type

template <class _Ty>
_INLINE_VAR constexpr bool is_arithmetic_v = // determine whether _Ty is an arithmetic type
    is_integral_v<_Ty> || is_floating_point_v<_Ty>;
//Whether the calls are integer or floating point, respectively, is true as long as one returns true.
template <class _Ty>
struct is_arithmetic : bool_constant<is_arithmetic_v<_Ty>> {};
//Same as above

10. remove_reference: Remove references and do not change original attributes plus const attribute

template <class _Ty>
struct remove_reference {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty;
};
//Non-Left and Right References
template <class _Ty>
struct remove_reference<_Ty&> {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty&;
};
//Specified Left Value Reference
template <class _Ty>
struct remove_reference<_Ty&&> {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty&&;
};
//Specified Right Value Reference
template <class _Ty>
using remove_reference_t = typename remove_reference<_Ty>::type;
//Remove the referenced type for return
template <class _Ty>
using _Const_thru_ref = typename remove_reference<_Ty>::_Const_thru_ref_type;
//Add const attribute to the original attribute
template <class _Ty>
using _Remove_cvref_t = remove_cv_t<remove_reference_t<_Ty>>;
//Remove: type, defined as the style of the type, normalize xxx_ Does t represent a type.
#if _HAS_CXX20
template <class _Ty>
using remove_cvref_t = _Remove_cvref_t<_Ty>;
//Ditto
template <class _Ty>
struct remove_cvref {
    using type = remove_cvref_t<_Ty>;
};
//This should be for internal use within the template in the uniform format xx::type.

Note: I can't read the article about template type matching in which I detailed the derivation of template type. link

summary

Finally, all the template bases have been resolved. Train_to-be-handled The two implementations of type and tuple are parsed out or know most of the principles (that is, the template should actually be). After reading these, you can only understand the templates. It's far from enough to use them, but when you train_ After learning type and tuple, I believe that using templates is definitely not just a matter of understanding.

Topics: C++