简体   繁体   English

递归模板类型声明

[英]Recursive template type declaration

I am trying to create a struct that contains a N nested templated types:我正在尝试创建一个包含 N 嵌套模板类型的结构:

The code I have to do this is:我必须这样做的代码是:


// slab (base case essentially)
template<typename T, uint32_t nvecs = 8, align_policy ap = align_policy::none>
struct slab {
T t;
};

// wrapper for either slab or other super_slabs
template<typename T,
         uint32_t nvecs        = 8,
         uint32_t inner_nvec   = 8,
         typename inner_slab_t = slab<T, inner_nvec>>
struct super_slab {
inner_slab_t ist;
};

// hopefully correct functions to extract Nth values for argument pack
template<typename... Vals>
constexpr uint32_t
_get_0(uint32_t v, Vals... vals) {
    return v;
}

template<typename... Vals>
constexpr uint32_t
_get_N(int32_t n, uint32_t v, Vals... vals) {
    return n <= 0 ? _get_0(vals...) : _get_N(n - 1, vals...);
}

template<typename... Vals>
constexpr uint32_t
get_N(int32_t n, Vals... vals) {
    return _get_N(n, vals...);
}


// first approach I tried
#ifdef APPROACH_A
template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
struct type_helper {
    using type = typename std::conditional<
        level <= 1,
        slab<T, other_nvecs...>,
        super_slab<T,
                   get_N(nlevels - level, other_nvecs...),
                   get_N(nlevels - (level + 1), other_nvecs...),
                   typename type_helper<T, nlevels, level - 1, other_nvecs...>::
                       type>>::type;
};
#endif

// second approach I tried
#ifdef APPROACH_B
template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
struct type_helper;
template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
struct type_helper {
    using type = super_slab<
        T,
        get_N(nlevels - level, other_nvecs...),
        get_N(nlevels - (level + 1), other_nvecs...),
        typename type_helper<T, nlevels, level - 1, other_nvecs...>::type>;
};

template<typename T, uint32_t nlevels, uint32_t level, uint32_t other_nvecs>
struct type_helper {
    using type = slab<T, nvecs>;
};
#endif

// struct I want to use for API.
template<typename T, uint32_t levels, uint32_t... level_nvecs>
struct slab_manager {
    using slab_t =
        typename type_helper<T, levels, levels, level_nvecs...>::type;
};

APPROACH_A compiles but when I try to instantiate it with: APPROACH_A 可以编译,但是当我尝试使用以下命令实例化它时:

slab_manager<uint64_t, 1, 8> m;

I get the error:我得到错误:

slab_manager.h: In instantiation of ‘struct type_helper<long unsigned int, 1, 4294966399, 8>’:
slab_manager.h:39:36:   recursively required from ‘struct type_helper<long unsigned int, 1, 0, 8>’
slab_manager.h:39:36:   required from ‘struct type_helper<long unsigned int, 1, 1, 8>’
slab_manager.h:63:70:   required from ‘struct slab_manager<long unsigned int, 1, 8>’
obj_slab_test.cc:213:34:   required from here
slab_manager.h:36:25: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
                    get_N(nlevels - level, other_nvecs...),
                    ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

I don't really see how 'struct type_helper<long unsigned int, 1, 4294966399, 8>': ever comes to pass as passing 1 is initial level should cause it to just hit the base case in std::conditional .我真的不明白'struct type_helper<long unsigned int, 1, 4294966399, 8>':是如何实现的,因为传递1是初始级别应该导致它在std::conditional中达到基本情况。 My guess was the issue was std::conditional fully unrolls both options but when I try approach BI get the following errors:我的猜测是问题是std::conditional完全展开两个选项但是当我尝试方法 BI 得到以下错误:

slab_manager.h:43:68: error: template parameter ‘unsigned int ...other_nvecs’
 template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
                                                                    ^~~~~~~~~~~
slab_manager.h:55:8: error: redeclared here as ‘unsigned int other_nvecs’
 struct type_helper {
        ^~~~~~~~~~~
slab_manager.h: In instantiation of ‘struct type_helper<long unsigned int, 1, 4294966399, 8>’:
slab_manager.h:51:75:   recursively required from ‘struct type_helper<long unsigned int, 1, 0, 8>’
slab_manager.h:51:75:   required from ‘struct type_helper<long unsigned int, 1, 1, 8>’
slab_manager.h:63:70:   required from ‘struct slab_manager<long unsigned int, 1, 8>’
obj_slab_test.cc:213:34:   required from here
slab_manager.h:49:14: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
         get_N(nlevels - level, other_nvecs...),
         ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Which indicates the reason A was failing is NOT due to std::conditional but confuses me more because the method of re-declaring types with a base case was what I saw in every guide I've seen so far.这表明 A 失败的原因不是由于std::conditional而是让我更加困惑,因为使用基本案例重新声明类型的方法是我在迄今为止看到的每个指南中看到的。

My guess is that calling slab_manager<uint64_t, 1, 8> m;我的猜测是调用slab_manager<uint64_t, 1, 8> m; would set slab_t = slab<uint64_t, 8> and for example slab_manager<uint64_t, 3, 2, 4, 8> m;将设置slab_t = slab<uint64_t, 8>例如slab_manager<uint64_t, 3, 2, 4, 8> m; would set slab_t = super_slab<uint64_t, 2, 4, super_slab<uint64_t, 4, 8, slab<uint64_t, 8> .将设置slab_t = super_slab<uint64_t, 2, 4, super_slab<uint64_t, 4, 8, slab<uint64_t, 8> Any help would be appreciated.任何帮助,将不胜感激。 Thank you!谢谢!

Edit for posterity:为后代编辑:

There was ALOT wrong with this question.这个问题有很多错误。 as @cdhowie pointed out I wasnt following the guides right.正如@cdhowie 指出的那样,我没有正确遵循指南。 Here is a solution that worked:这是一个有效的解决方案:

template<typename... Vals>
constexpr int32_t
_get_0(int32_t v, Vals... vals) {
    return v;
}


template<typename... Vals>
constexpr int32_t
_get_0(int32_t v) {
    return v;
}

template<typename... Vals>
constexpr int32_t
_get_N(int32_t n, Vals... vals) {
    return n <= 0 ? _get_0(vals...) : _get_N(n - 1, vals...);
}


template<typename... Vals>
constexpr int32_t
get_N(int32_t n, Vals... vals) {
    return _get_N(n, vals...);
}


template<typename T, int32_t nlevels, int32_t level, int32_t... other_nvecs>
struct type_helper;


template<typename T, int32_t nlevel, int32_t... other_nvecs>
struct type_helper<T, nlevel, 0, other_nvecs...> {
    typedef slab<T, get_N(nlevel, other_nvecs...)> type;
};

template<typename T, int32_t nlevels, int32_t level, int32_t... other_nvecs>
struct type_helper {
    typedef super_slab<T,
                       get_N(nlevels - level, other_nvecs...),
                       get_N(nlevels - (level + 1), other_nvecs...),
                       typename type_helper<T, nlevels, level - 1, other_nvecs...>::type>
        type;
};


template<typename T, int32_t levels, int32_t... level_nvecs>
struct slab_manager {
    using slab_t = typename type_helper<T, levels, levels - 1, level_nvecs...>::type;
};

In approach A, you have compile-time integer underflow since both types in the two "branches" of std::conditional are still instantiated, even if the conditional selects the other.在方法 A 中,您有编译时 integer 下溢,因为std::conditional的两个“分支”中的两种类型仍然被实例化,即使条件选择了另一个。 As a result, your type_helper causes infinite recursion trying to instantiate.结果,您的type_helper会导致尝试实例化的无限递归。

The compilation error with approach B is that your syntax for specialization is wrong.方法 B 的编译错误是您的专业化语法错误。 This:这个:

template<typename T, uint32_t nlevels, uint32_t level, uint32_t other_nvecs>
struct type_helper {
    using type = slab<T, nvecs>;
};

Should be this:应该是这样的:

template<typename T, uint32_t nlevels, uint32_t level, uint32_t other_nvecs>
struct type_helper<T, nlevels, level, other_nvecs> {
    using type = slab<T, nvecs>;
};

Approach B falls prey to exactly the same infinite recursion problem as approach A: type_helper<T, nlevels, level, other_nvecs...> will always instantiate type_helper<T, nlevels, level - 1, other_nvecs...> .方法 B 陷入与方法 A 完全相同的无限递归问题: type_helper<T, nlevels, level, other_nvecs...>将始终实例化type_helper<T, nlevels, level - 1, other_nvecs...> level underflows, and recursion continues until the compiler gives up. level下溢,并且递归继续,直到编译器放弃。

The way you'd terminate recursion in this case would be to define a special case when level is zero:在这种情况下终止递归的方法是在level为零时定义一个特殊情况:

template<typename T, uint32_t nlevels, uint32_t... other_nvecs>
struct type_helper<T, nlevels, 0, other_nvecs...> {
    using type = // whatever makes sense in your case
};

I don't know what type should be here (but I presume you do).我不知道这里应该是什么type (但我想你知道)。

You can also have a final terminating case where level is zero and other_nvecs is empty:您还可以有一个最终终止情况,其中level为零且other_nvecs为空:

template<typename T, uint32_t nlevels>
struct type_helper<T, nlevels, 0> {
    using type = // something
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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