簡體   English   中英

依賴的非類型模板參數和可變參數模板

[英]Dependant non-type template parameter and variadic template

我試圖用一個名為integer_range的新類擴展std::integer_sequence提供的可能性(它可以在兩個邊界之間創建一個整數序列)。 我的實現是基於我對這個問題的回答 ,我試圖適應std::integer_sequence

namespace details
{
    template<typename Int, Int C, Int P, Int... N>
    struct increasing_integer_range:
        increasing_integer_range<Int, C-1, P+1, N..., P>
    {};

    template<typename Int, Int C, Int P, Int... N>
    struct decreasing_integer_range:
        decreasing_integer_range<Int, C+1, P-1, N..., P>
    {};

    template<typename Int, Int P, Int... N>
    struct increasing_integer_range<Int, 0, P, N...>:
        std::integer_sequence<Int, N...>
    {};

    template<typename Int, Int P, Int... N>
    struct decreasing_integer_range<Int, 0, P, N...>:
        std::integer_sequence<Int, N...>
    {};
}

template<typename Int, Int S, Int E,  bool Increasing=(S<E)>
struct integer_range;

template<typename Int, Int S, Int E>
struct integer_range<Int, S, E, true>:
    details::increasing_integer_range<Int, std::integral_constant<Int, E-S>, S>
{};

template<typename Int, Int S, Int E>
struct integer_range<Int, S, E, false>:
    details::decreasing_integer_range<Int, std::integral_constant<Int, E-S>, S>
{};

template<std::size_t S, std::size_t E>
using index_range = integer_range<std::size_t, S, E>;

我認為更改將是微不足道的(添加typename模板參數),但這實際上在0化中引入了依賴的非類型參數的問題。 這是編譯器錯誤:

error: type `Int` of template argument `0` depends on a template parameter

基本問題已經有了一些解決方案。 但是,由於我使用了可變參數模板,因此修復起來更加困難: 這個答案無法正常工作,因為我不允許在可變參數模板之后使用默認模板參數。 因此,我嘗試實現接受的答案中提到的修復,但似乎正如評論中所提到的,我的編譯器(g ++ 4.8.1)無法消除歧義並認為以下兩個專業化都是同樣專業的:

  • struct increasing_integer_range<Int, std::integral_constant<Int, C>, P, N...>: /* */
  • struct increasing_integer_range<Int, std::integral_constant<Int, 0>, P, N...>: /* */

有沒有其他方法來解決這個問題? 我沒有想法。

我只是在等待Yakk突然冒出來並提供一個非hacky更好的解決方案;) (我太累了,無法想出更好的東西..)但是,這很適用於g ++ 4.8.1 :

#include <type_traits>

// C++1y Standard Library
// use this ONLY FOR TESTING PURPOSES
namespace std
{
    template<class T, T... vals> struct integer_sequence {};
}

namespace details
{
    template<typename T>
    struct minus_helper;
    template<typename T, T val>
    struct minus_helper< std::integral_constant<T, val> >
    {
        using type = std::integral_constant<T, val-1>;
    };

    template<typename T>
    using minus = typename minus_helper<T>::type;

    template<typename Int, typename C, Int P, Int... N>
    struct increasing_integer_range :
        increasing_integer_range<Int, minus<C>, P+1, N..., P>
    {};

    template<typename Int, Int P, Int... N>
    struct increasing_integer_range<Int, std::integral_constant<Int, 0>,
                                    P, N...>
        : std::integer_sequence<Int, N...>
    {};
}

template<typename Int, Int S, Int E,  bool Increasing=(S<E)>
struct integer_range;

template<typename Int, Int S, Int E>
struct integer_range<Int, S, E, true>:
    details::increasing_integer_range<Int, std::integral_constant<Int, E-S>, S>
{};

template<std::size_t S, std::size_t E>
using index_range = integer_range<std::size_t, S, E>;

int main()
{
    index_range<1, 5> f;
}

我認為可以做得更好的原因是, 似乎增加和減少范圍的實現是多余的。 但在提供統一解決方案之前,我可能不得不考慮角落情況(溢出)。

例如,您可以使用std::make_integer_range加上部分特化加包擴展輕松創建一個移位的整數范圍。 但這可能會溢出。

我只是將你的integer_range減少為對std::integer_sequence的單個非遞歸調用:

namespace details
{
    template<typename Int, typename, Int S>
    struct increasing_integer_range;

    template<typename Int, Int... N, Int S>
    struct increasing_integer_range<Int, std::integer_sequence<Int, N...>, S>
        : std::integer_sequence<Int, N+S...>
    {};

    template<typename Int, typename, Int S>
    struct decreasing_integer_range;

    template<typename Int, Int... N, Int S>
    struct decreasing_integer_range<Int, std::integer_sequence<Int, N...>, S>
        : std::integer_sequence<Int, S-N...>
    {};
}

template<typename Int, Int S, Int E,  bool Increasing=(S<E)>
struct integer_range;

template<typename Int, Int S, Int E>
struct integer_range<Int, S, E, true>:
    details::increasing_integer_range<Int, std::make_integer_sequence<Int, E-S>, S>
{};

template<typename Int, Int S, Int E>
struct integer_range<Int, S, E, false>:
    details::decreasing_integer_range<Int, std::make_integer_sequence<Int, S-E>, S>
{};

template<std::size_t S, std::size_t E>
using index_range = integer_range<std::size_t, S, E>;

我測試過的是:

template<std::size_t... N>
void dummy( const std::integer_sequence< std::size_t, N... >& );

int main()
{
    dummy( index_range< 2, 5 >() );
    dummy( index_range< 5, 2 >() );
}

獲得預期的鏈接器錯誤:

main.cpp:(.text.startup+0xa): undefined reference to `void dummy<2ul, 3ul, 4ul>(detail::integer_sequence<unsigned long, 2ul, 3ul, 4ul> const&)'
main.cpp:(.text.startup+0x14): undefined reference to `void dummy<5ul, 4ul, 3ul>(detail::integer_sequence<unsigned long, 5ul, 4ul, 3ul> const&)'

實例 (使用自己的integer_sequence實現,只跳過第一部分)

例如,您可以使用std::make_integer_range加上部分特化加包擴展輕松創建一個移位的整數范圍。

簡要演示。

template<typename T>
constexpr T abs(T t)
{ return t < static_cast<T>(0) ? -t : t; }

template<typename Sequence>
struct match_sequence {};

template<typename Int, Int... Ns>
struct match_sequence<std::integer_sequence<Int, Ns...>> {
    template<Int Base, Int Stride = static_cast<Int>(1)>
    using Offset = std::integer_sequence<Int, Base + Stride * Ns...>;
};

template<typename Int, Int Low, Int High>
struct integer_range {
private:
    static constexpr auto width = abs(High - Low);
    using base = std::make_integer_sequence<Int, width>;
    static constexpr bool increasing = High > Low;
    static constexpr auto stride = increasing ? static_cast<Int>(1) : static_cast<Int>(-1);

public:
    using type = typename match_sequence<base>::template Offset<Low, stride>;
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM