Зависимый не типовой параметр шаблона и шаблон переменной

Я пытаюсь расширить возможности, предлагаемые std::integer_sequence с новым классом по имени integer_range (который явно создает последовательность целых чисел между двумя границами). Моя реализация была основана на моем ответе этот вопрос что я пытался приспособиться к 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...>: /* */

Есть ли другой способ исправить эту проблему? У меня нет идей.

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&)'

Живой пример (с собственной реализацией integer_sequenceПропусти первую часть)

3

Другие решения

Я просто жду, когда Якк внезапно появится из ниоткуда и предоставит нехакерское лучшее решение;) (Я слишком устал, чтобы придумать что-то лучшее ..) но, увы, это работает для 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 плюс частичная специализация плюс расширение пакета. Но это может переполниться.

3

Например, вы можете легко сделать смещенный диапазон целых чисел, используя
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>;
};
2
По вопросам рекламы [email protected]