计算嵌套包中的类型总数

时间:2015-01-22 15:23:13

标签: c++ templates c++11 variadic

NumTypes<Args...>::value是给出Args...中的类型总数,包括嵌套包中的所有类型(如果有的话),例如如果

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

然后NumTypes<T, int, char>::value将为13(我们不计算包装类本身)。以下代码可以正常工作,但是当我用std::string替换任何类型时,我得到了一系列永远不会终止的std::allocator错误(使用GCC 4.8.1)。我怀疑其他类型会产生相同的错误。为什么?以及如何修复代码以避免这种奇怪的错误?

#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
}

2 个答案:

答案 0 :(得分:2)

std::string实际上是std::basic_string<char, char_traits<char>, allocator<charT>> 它的声明有默认模板参数:

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

你的递归是错误的,并且你有无限循环(当你从basic_string<charT, traits, Alloc>删除第一个参数时,你得到了basic_string<traits, Alloc, allocator<traits>>)。

您可以通过从等式中删除P来解决此问题:

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

答案 1 :(得分:1)

std::stringstd::basic_string<char>的typedef,因此与您打算用于分组类型的IsPackNumTypes的特化相匹配。

您必须专门针对分组或模板叶片类型,无论哪种情况下您需要花费更少的精力:

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
}