简体   繁体   中英

Counting the total number of types in a nested pack

NumTypes<Args...>::value is to give the total number of types in Args... , including all types in a nested packs (if any), eg if

using T = Group<int, bool, Wrap<char, Pack<char, long, Group<char, Object, short>, short>, double>, long>;

then NumTypes<T, int, char>::value will be 13 (we do not count the wrapper classes themselves). The following code works correctly, but when I replace any of the types with std::string I get a flurry of std::allocator errors that never terminates (with GCC 4.8.1). I suspect other types will generate the same error. Why? And how to fix the code to avoid this weird error?

#include <iostream>
#include <string>

#define show(variable) std::cout << #variable << " = " << variable << std::endl;

template <typename T>
struct IsPack {
    static const bool value = false;
};

template <template <typename...> class P, typename... Args>
struct IsPack<P<Args...>> {
    static const bool value = true;
};

template <typename...> struct NumTypes;

template <typename T>
struct NumTypes<T> {
    static const int value = 1;
};

template <template <typename...> class P>
struct NumTypes<P<>> { static const int value = 0; };

template <template <typename...> class P, typename First, typename... Rest>
struct NumTypes<P<First, Rest...>> {
    static const int value = IsPack<First>::value ?
        NumTypes<First>::value + NumTypes<P<Rest...>>::value :
        1 + NumTypes<P<Rest...>>::value;
};

template <typename First, typename... Rest>
struct NumTypes<First, Rest...> {
    static const int value = NumTypes<First>::value + NumTypes<Rest...>::value;
};

template <typename...> struct Pack;
template <typename...> struct Group;
template <typename...> struct Wrap;
struct Object {};

int main() {
    using A = Pack<int, Object, long>;
    show (NumTypes<A>::value)  // 3

    using B = Pack<int, bool, Pack<char, Object>, long>;
    show (NumTypes<B>::value)  // 5

    using C = Group<int, bool, Wrap<char, Pack<char, long, Group<char, Object, short>, short>, double>, long>;
    show (NumTypes<C>::value)  // 11

    using D = Group<Pack<int, Object, double>, bool, Wrap<char, Pack<char, double, Group<char, Pack<char, long, short>, int, Object>, short>, double>, long>;
    show (NumTypes<D>::value)  // 16

    std::cout << NumTypes<A, B, int, char, C, Object, D>::value << std::endl;  // 38
}

std::string is in fact std::basic_string<char, char_traits<char>, allocator<charT>> .
And its declaration has default template argument:

template <class charT,
          class traits = char_traits<charT>,
          class Alloc = allocator<charT>>
class basic_string;

Your recursion is so wrong, And you have infinite loop (when you remove the first parameter from basic_string<charT, traits, Alloc> , you got basic_string<traits, Alloc, allocator<traits>> ).

You may fix that by removing P from the equation:

template <template <typename...> class P, typename First, typename... Rest>
struct NumTypes<P<First, Rest...>> {
    static const int numInFirst = NumTypes<First>::value;
    static const int value = numInFirst + NumTypes<std::tuple<Rest...>>::value;
};

Live example

std::string is a typedef for std::basic_string<char> , and thus matches specializations of IsPack and NumTypes you intended for grouping types.

You'll have to specialize either for grouping or template leaf types, whichever'll take less effort in your case:

template <typename T>
struct IsPack
    : std::false_type
{};


template <typename...> 
struct NumTypes;

template <typename T, bool is_pack> 
struct NumTypesHelper;

template <typename T>
struct NumTypesHelper<T, false>
    : std::integral_constant<int, 1>
{};

template <template <typename...> class P, typename... Args>
struct NumTypesHelper<P<Args...>, true>
    : NumTypes<Args...>
{};

template <>
struct NumTypes<>
    : std::integral_constant<int, 0>
{};

template <typename First, typename... Rest>
struct NumTypes<First, Rest...>
    : std::integral_constant<int, NumTypesHelper<First, IsPack<First>::value>::value + NumTypes<Rest...>::value>
{};

template <typename...> struct Pack;
template <typename...> struct Group;
template <typename...> struct Wrap;
struct Object {};

template <typename... Args>
struct IsPack<Pack<Args...>>
    : std::true_type
{};

template <typename... Args>
struct IsPack<Group<Args...>>
    : std::true_type
{};

template <typename... Args>
struct IsPack<Wrap<Args...>>
    : std::true_type
{};

#define show(variable) std::cout << #variable << " = " << variable << std::endl;

int main() {
    using A = Pack<int, Object, long>;
    show (NumTypes<A>::value)  // 3

    using B = Pack<int, bool, Pack<char, double>, long>;
    show (NumTypes<B>::value)  // 5

    using C = Group<int, bool, Wrap<char, Pack<char, long, Group<char, Object, short>, short>, double>, long>;
    show (NumTypes<C>::value)  // 11

    using D = Group<Pack<int, Object, double>, bool, Wrap<char, Pack<char, double, Group<char, Pack<char, long, short>, int, Object>, short>, double>, long>;
    show (NumTypes<D>::value)  // 16
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM