在编译时比较两个整数序列?

时间:2015-09-29 16:24:28

标签: c++ templates c++14 variadic-templates

我们说我有constexpr std::integer_sequence<...>个对象。在编译时,我对它执行一些操作,然后我想static_assert它是==其他std::integer_sequence<...>。鉴于integer_sequence是一种类型,我如何提供适当比较它们的重载constexpr bool operator==

更具体的示例:将int转换为std::integer_sequence<char>。也就是说,将整数转换为字符序列(inspired by Peter Sommerlad's talk at CPPCon '15

我有一些功能,我非常有信心将小于1000的十进制整数值适当地转换为4个元素的字符序列:

#include <utility> // integer_sequence

template<char... t>
using char_sequence = std::integer_sequence<char, t...>;
constexpr char make_digit_char(const size_t digit, const size_t power_of_ten=1, const char zero_replacement = ' ')
{
    return char(digit>=power_of_ten?digit/power_of_ten+'0':zero_replacement);
}

template<int num>
constexpr auto int_to_char_sequence()
{
    static_assert(num < 1000, "Cannot handle integers larger than 1000!");
    //format for up to 1000
    return char_sequence<' ', 
                    make_digit_char(num,100), 
                    make_digit_char(num%100,10,num>=100?'0':' '),
                    '0' + num % 10>{};
}

但是,我不相信自己,所以我想写一些测试:

static_assert(char_sequence<' ', ' ', ' ', '0'>{} == int_to_char_sequence<0>(), "failed to convert 0 to char sequence");
static_assert(char_sequence<' ', ' ', ' ', '1'>{} == int_to_char_sequence<1>(), "failed to convert 1 to char sequence");
// ...
static_assert(char_sequence<' ', '1', '1', '1'>{} == int_to_char_sequence<111>(), "failed to convert 111 to char sequence")

并且还想测试!=

// ...
static_assert(char_sequence<' ', '1', '1', '1', '2'>{} != int_to_char_sequence<111>(), " 1 1 1 2 should not be equal to 111");
static_assert(int_to_char_sequence<111>() != char_sequence<' ', '1', '1', '1', '2'>{}, " 111 should not be equal to  1 1 1 2");

所以我对等价运算符有一些要求:

  • 序列中的数据存储在类型中,因此两个字符序列基本上是不同的类型
  • 如果一个字符序列比另一个字符序列长,怎么办?
  • 运营商需要constexpr才能使static_assert正常运作
    • 表示我们无法进行任何类型的转化std::array并进行比较

我如何做到这一点?

作者注意: 我没有在SO上发现另一篇可以对整数序列进行编译时相等的帖子,所以我在下面回答了我自己的问题。这是我自己完成的工作,我绝不认为它是最佳方法。如果您有更好的方法,请将其作为另一个答案发布,我会接受它!

3 个答案:

答案 0 :(得分:9)

这是一个较短的版本:

template <char... A, char... B>
constexpr bool operator==(char_sequence<A...>, char_sequence<B...>)
{
    return std::is_same<char_sequence<A...>, char_sequence<B...>>::value;
}

当且仅当由这些序列组成的类型相同时,序列是相同的。

虽然通常情况下,您只是直接测试一下:

template <int num>
using int_to_char_sequence_t = decltype(int_to_char_sequence<num>());

static_assert(std::is_same<
    int_to_char_sequence_t<0>,
    char_sequence<' ', ' ', ' ', '0'>
    >::value, "!");

答案 1 :(得分:2)

如果可以,请避免将类型信息降级为constexpr数据。

其次,在==或内置的仅依赖于模板和类型的类型上重载std会使您的程序格式错误,无需诊断,在最新的标准草案中。

template<class T, T...ts>
struct sequence: std::integral_sequence<T,ts...> {
  constexpr sequence(){};
};
template<char...cs>
using chars = sequence<char, cs...>;

template<bool b>
using bool_t = std::integral_constant<bool, b>;

template<class T, T...as, T...bs>
bool_t<
  std::is_same< sequence<T, as...>, sequence<T,bs...>{}
> operator==( sequence<T, as...>, sequence<T, bs...> ) {
  return {};
}

如果操作相同则返回std::true_type,否则返回std::false_type

如果在bool上下文中使用,则constexpr operator bool()const执行正确的操作。如果在其他上下文中使用,则信息将保留为类型,而不会降级为仅仅编译时的值。

答案 2 :(得分:1)

为了处理编译时比较的情况,我们将使用一个可变参数函数,它允许我们从序列中拉出chars,比较它们,然后递归到其余的序列。

  • 基本案例:空序列(返回true)
  • 基本情况:一个序列比另一个序列长(返回false)
  • 递归情况:非空序列:比较序列头,然后递归

  • 根据!=运营商

  • 实施==运营商

代码:

// empty character sequences
constexpr bool operator == (char_sequence<>, char_sequence<>)
{
    return true;
}

// character sequences with one element
template<char T, char... t>
constexpr bool operator == (char_sequence<T, t...>, char_sequence<>)
{
    return false;
}

template<char T, char... t>
constexpr bool operator == (char_sequence<>, char_sequence<T, t...>)
{
    return false;
}

// Recursive case
template<char S, char... s, char T, char... t>
constexpr bool operator == (char_sequence<S, s...>, char_sequence<T, t...>)
{
    return S == T && char_sequence<s...>{} == char_sequence<t...>{};
}

// operator != in terms of operator==
template<char... s, char... t>
constexpr bool operator != (char_sequence<s...>, char_sequence<t...>)
{
    return !(char_sequence<s...>{} == char_sequence<t...>{});
}

为了便于阅读,缩短了代码;在定义模板方法之前,应始终声明模板方法。

Live Demo