在编译时比较两个整数序列?
Compare two integer sequences at compile-time?
假设我有一个 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 上找到另一个可以对整数序列进行编译时相等的 post,所以我在下面回答了我自己的问题。这是我自己完成的工作,我绝不声称这是最佳方法。如果您有更好的方法,请 post 作为另一个答案,我会接受它!
为了处理编译时比较的情况,我们将使用一个可变参数函数,它允许我们从序列中提取 chars
,比较它们,然后对其余序列进行递归。
- 基本情况:空序列(return 正确)
- 基本情况:一个序列比另一个长(return 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...>{});
}
为了便于阅读,代码被缩短了;您应该始终在定义模板方法之前对其进行声明。
这是一个较短的版本:
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, "!");
尽可能避免将类型信息降级为 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 {};
}
this returns a std::true_type
if the operation is equivalent, and std::false_type
otherwise.
如果在 bool
上下文中使用,他们 constexpr operator bool()const
可以做正确的事情。如果在其他上下文中使用,信息将保留为一种类型,而不会降级为纯粹的编译时值。
假设我有一个 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 上找到另一个可以对整数序列进行编译时相等的 post,所以我在下面回答了我自己的问题。这是我自己完成的工作,我绝不声称这是最佳方法。如果您有更好的方法,请 post 作为另一个答案,我会接受它!
为了处理编译时比较的情况,我们将使用一个可变参数函数,它允许我们从序列中提取 chars
,比较它们,然后对其余序列进行递归。
- 基本情况:空序列(return 正确)
- 基本情况:一个序列比另一个长(return 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...>{});
}
为了便于阅读,代码被缩短了;您应该始终在定义模板方法之前对其进行声明。
这是一个较短的版本:
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, "!");
尽可能避免将类型信息降级为 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 {};
}
this returns a std::true_type
if the operation is equivalent, and std::false_type
otherwise.
如果在 bool
上下文中使用,他们 constexpr operator bool()const
可以做正确的事情。如果在其他上下文中使用,信息将保留为一种类型,而不会降级为纯粹的编译时值。