簡體   English   中英

遞歸模板類型聲明

[英]Recursive template type declaration

我正在嘗試創建一個包含 N 嵌套模板類型的結構:

我必須這樣做的代碼是:


// 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 可以編譯,但是當我嘗試使用以下命令實例化它時:

slab_manager<uint64_t, 1, 8> m;

我得到錯誤:

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.

我真的不明白'struct type_helper<long unsigned int, 1, 4294966399, 8>':是如何實現的,因為傳遞1是初始級別應該導致它在std::conditional中達到基本情況。 我的猜測是問題是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...),
         ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

這表明 A 失敗的原因不是由於std::conditional而是讓我更加困惑,因為使用基本案例重新聲明類型的方法是我在迄今為止看到的每個指南中看到的。

我的猜測是調用slab_manager<uint64_t, 1, 8> m; 將設置slab_t = slab<uint64_t, 8>例如slab_manager<uint64_t, 3, 2, 4, 8> m; 將設置slab_t = super_slab<uint64_t, 2, 4, super_slab<uint64_t, 4, 8, slab<uint64_t, 8> 任何幫助,將不勝感激。 謝謝!

為后代編輯:

這個問題有很多錯誤。 正如@cdhowie 指出的那樣,我沒有正確遵循指南。 這是一個有效的解決方案:

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

在方法 A 中,您有編譯時 integer 下溢,因為std::conditional的兩個“分支”中的兩種類型仍然被實例化,即使條件選擇了另一個。 結果,您的type_helper會導致嘗試實例化的無限遞歸。

方法 B 的編譯錯誤是您的專業化語法錯誤。 這個:

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

應該是這樣的:

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

方法 B 陷入與方法 A 完全相同的無限遞歸問題: type_helper<T, nlevels, level, other_nvecs...>將始終實例化type_helper<T, nlevels, level - 1, other_nvecs...> level下溢,並且遞歸繼續,直到編譯器放棄。

在這種情況下終止遞歸的方法是在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
};

我不知道這里應該是什么type (但我想你知道)。

您還可以有一個最終終止情況,其中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