【问题标题】:Dependant non-type template parameter and variadic template依赖非类型模板参数和可变参数模板
【发布时间】:2014-06-07 09:02:47
【问题描述】:

我正在尝试通过一个名为integer_range 的新类来扩展std::integer_sequence 提供的可能性(它显然会在两个边界之间创建一个整数序列)。我的实现基于我对this question 的回答,我试图适应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

基本问题already has一些解决方法。但是,由于我使用可变参数模板,修复变得更加困难:this answer 无法工作,因为我不允许在可变参数模板之后有默认模板参数。因此,我尝试实现the accepted answer 中提到的修复程序,但似乎正如 cmets 中所述,我的编译器 (g++ 4.8.1) 无法消除歧义,并认为以下两个专业化都同样专业化:

  • struct increasing_integer_range&lt;Int, std::integral_constant&lt;Int, C&gt;, P, N...&gt;: /* */
  • struct increasing_integer_range&lt;Int, std::integral_constant&lt;Int, 0&gt;, P, N...&gt;: /* */

还有其他方法可以解决这个问题吗?我没有想法。

【问题讨论】:

  • this 是否不适用于 g++4.8.1?
  • 如果不行,你可以试试this。
  • “不允许在可变参数模板之后有默认模板参数” 如果你包装序列并使用部分特化(例如传递已经构建的序列),这是可能的作为std::integer_sequence)。
  • @dyp 实例化的话,第一个does not work。
  • 哎呀,是的,我一直忘记 typedef 不实例化。不过,第二个有效(coliru 使用 g++4.8.2)。

标签: c++ templates template-specialization c++14 indices


【解决方案1】:

我只是在等待 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 加上部分特化和包扩展来轻松地制作移位的整数范围。但这可能会溢出。

【讨论】:

  • 如果你觉得可以更好,我会稍等片刻接受,但无论如何已经很好了:)
  • 今天不会好转了。也许明天。
【解决方案2】:

例如,您可以使用 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>;
};

【讨论】:

  • 有了这个修复,it compiles(尽管我的 hacky make_integer_sequence 被 g++ 拒绝,因为 Morwenn 在 OP 中遇到的问题完全相同——但我猜这不是问题)。
  • 这很有趣。但是目前,它有一个问题:integer_range 不继承自/别名到std::integer_sequence。我们只需要一个别名模板来解决这个问题。
  • @dyp 顺便问一下,编译器有没有bug?
【解决方案3】:

我会将您的 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&)'

Live example(有自己的integer_sequence实现,跳过第一部分)

【讨论】:

  • 嘿!你不是 Yakk.. ;)
  • 这 确实 似乎是一个更干净且不那么骇人听闻的解决方案:)
  • 由于我对上述代码的实验(我在编写实时示例时搞砸了,混淆了index_sequence 和integer_sequence),我刚刚提交了Bugzilla #60942 :)跨度>
猜你喜欢
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-03-18
  • 1970-01-01
相关资源
最近更新 更多