当我想在我的std::bitset中使用__uint128而不是GCC的D1时,我发现它很难使用,因为没有办法将一个位集扩展或截断到另一个大小。也不能使用不同宽度的位集进行逐位算术(&、|、^)或比较(==、!=)。
我已经确定了我认为应该是std::bitset的一部分的函数,并通过包装标准类来实现它们。为了在不同宽度之间进行转换,我不得不通过std::string,但是(正如注释中所写的),对std::bitset的更新将有一个更直接(且无异常)的路径。
我本可以创建而不是继承std::bitset --这在这里并不是真正值得关注的。请忽略处理my::bitset与std::bitset不同的机制,并设想它们是一个单一的、更大的类。我最感兴趣的是函数签名是否正确和完整。
请注意(与整数一样),拓宽可以是隐式的,但是收缩操作需要是显式的。大多数明显的重复是为了处理这一区别。
#include
#include
#include
#include
namespace my {
template
struct bitset : public std::bitset
{
// forwarding of std::bitset constructors
bitset()
: std::bitset{}
{}
template
requires std::is_unsigned_v
bitset(T n)
: std::bitset{n}
{}
template< class CharT, class Traits, class Alloc >
explicit bitset(const std::basic_string& str,
typename std::basic_string::size_type pos = 0,
typename std::basic_string::size_type n =
std::basic_string::npos)
: std::bitset{str, pos, n}
{
}
// Widening and narrowing constructors, to be added to std::bitset
// widening conversion
template
requires (P <= N)
bitset(const std::bitset& n) noexcept
: std::bitset{}
{
std::size_t i = n.size();
while (i-- > 0) {
this->set(i, n.test(i));
}
}
// narrowing conversion
template
requires (P > N)
explicit bitset(const std::bitset& n) noexcept
: std::bitset{}
{
std::size_t i = this->size();
while (i-- > 0) {
this->set(i, n.test(i));
}
}
};
// Deduction guide for my::bitset
template
bitset(std::bitset) -> bitset;
// Free functions to be added to namespace std
// Deduction guide
template
requires std::is_unsigned_v
bitset(T) -> bitset;
// comparisons - widen as necessary
template
constexpr auto operator==(const bitset& a, const bitset& b)
requires (N != Q)
{
if constexpr (N > Q)
return a == bitset{b};
else
return bitset{a} == b;
}
template
constexpr auto operator!=(const bitset& a, const bitset& b)
{
return ! (a == b);
}
// bitwise-and produces the narrower type
// (a specific exception to "doing as the integers do")
template
constexpr auto operator&(const bitset& a, const bitset& b)
requires (N > Q)
{
return bitset(a) & b;
}
template
constexpr auto operator&(const bitset& a, const bitset& b)
requires (N < Q)
{
return a & bitset(b);
}
// bitwise-and assignment accepts a wider type
template
constexpr auto operator&=(bitset& a, const bitset& b)
requires (N != Q)
{
return a &= bitset(b);
}
// bitwise-or produces the wider type
template
constexpr auto operator|(const bitset& a, const bitset& b)
requires (N < Q)
{
return bitset{a} | b;
}
template
constexpr auto operator|(const bitset& a, const bitset& b)
requires (N > Q)
{
return a | bitset{b};
}
template
constexpr auto& operator|=(bitset& a, const bitset& b)
requires (N >= Q)
{
return a = a | bitset{b};
}
// bitwise-xor produces the wider type
template
constexpr auto operator^(const bitset& a, const bitset& b)
requires (N < Q)
{
return bitset{a} ^ b;
}
template
constexpr auto operator^(const bitset& a, const bitset& b)
requires (N > Q)
{
return a ^ bitset{b};
}
template
constexpr auto& operator^=(bitset& a, const bitset& b)
requires (N >= Q)
{
return a = a ^ bitset{b};
}
template
requires std::is_unsigned_v
constexpr auto operator^(const bitset& a, const T& b)
{
return a ^ bitset{b};
}
template
requires std::is_unsigned_v
constexpr auto operator^(const T& a, const bitset& b)
{
return bitset{a} ^ b;
}
}对于上述代码,我有一个简单的编译测试。标记为// new的所有混合宽度操作都不会使用std::bitset进行编译;所有未注释的语句在my::bitset中都很好。注释掉的语句将是错误(因为它们具有不强制转换的收缩转换)。#define NEW_BITSET
int main()
{
#ifdef NEW_BITSET
using my::bitset;
#else
using std::bitset;
#endif
// Lower-case variables are narrow; upper-case are wide
bitset<32> a{0xFFFF0000u};
bitset<64> B{a ^ 0xFFFF00u};
//bitset<32> b = B; // error
bitset<32> b{B}; // explicit conversion
//auto b = bitset<32>{B}; // error
bitset c = a & B; // new; c is bitset<32>
bitset D = a | B; // new; D is bitset<64>
bitset E = a ^ B; // new; E is bitset<64>
c &= b; // no change
c &= B; // new (unlike |= and ^=)
B &= c; // new
c |= b; // no change
//c |= B; // error
D |= b; // new
c ^= b; // no change
//c ^= B; // error
D ^= b; // new
D &= E; // no change
}我可能应该使用Detection成语来断言某些东西不应该有效,但我不得不逐个取消对// error行的注释,以证明它们是错误的。我特别希望在我的混合宽度操作的接口中发现任何错误或遗漏,但我很乐意接受任何批评。发布于 2019-06-14 07:31:48
一个可能让用户感到惊讶的方面是,运算符&总是缩小匹配类型。这与通常的“做整数所做的”的指导是不同的。
一方面,不要把资源浪费在永远为零的比特上是有道理的。我自己的一个用例是从130位值中提取一些或全部较低的26位位,所以期望26位的结果似乎是合理的。
另一方面,当移动结果时,它可能会捕获用户。这两项职能将产生不同的结果:
auto fun_i(std::uint32_t a, std::uint16_t b) {
return (a & b) << 16;
}
auto fun_b(my::bitset<32> a, my::bitset<16> b) {
return (a & b) << 16;
}总的来说,我认为在转移之前必须明确地扩大范围是在掩盖大量数字时为节省资源而付出的一个公平的代价,但这需要有很好的记录。我重视不同的意见(请在评论中)。
https://codereview.stackexchange.com/questions/222236
复制相似问题