我正在尝试使用名为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中提到的修复,但似乎正如评论中所提到的,我的编译器(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...>: /* */
有没有其他方法可以解决这个问题?我没有想法。
答案 0 :(得分:3)
我只是在等待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
加上部分特化加包扩展轻松创建一个移位的整数范围。但这可能会溢出。
答案 1 :(得分: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
,只跳过第一部分)
答案 2 :(得分: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>;
};