我需要一个变量函数来将位打包和解压缩为整数类型。这是我第一次尝试:
template<typename T>
constexpr T pack(bool b)
{
return b;
}
template<typename T, typename... Types>
constexpr T pack(bool b, Types... args)
{
return (b << sizeof...(Types)) | pack<T>(args...);
}
template<typename T, typename... Types>
void unpack(T packed, bool& b1)
{
b1 = packed & 1;
}
template<typename T, typename... Types>
void unpack(T packed, bool& b1, Types&... args)
{
b1 = packed & (1 << sizeof...(Types));
unpack(packed, args...);
}用法示例:
int main(void)
{
std::cout << pack<int>(1, 0, 0, 1, 0, 1, 1, 0) << std::endl; // 150
std::cout << pack<int>(1, 0, 1) << std::endl; // 5
int val = pack<int>(1, 0, 1);
bool b1, b2, b3;
unpack(val, b1, b2, b3);
std::cout << b1 << " " << b2 << " " << b3 << std::endl; // 1 0 1
}这段代码是否包含任何bug,可以改进,以及在这里是否适当地使用了各种模板(我仍然不掌握它们的语法)?
发布于 2014-04-04 17:53:26
我在这段代码中看到的一个严重缺陷是,用户需要确定unpack的参数数。对于输出依赖于运行时输入的函数,这是“否”。更糟糕的是,没有任何检查可以告诉用户,实际数字溢出了要解压缩的位数。
一个可能的解决方案是返回一个std::vector<bool>以适应整个数字。
另一种解决方案是保留当前接口,但引入错误通知(异常或返回代码)。
关于unpack函数的另一件事是:在递归结束时,您似乎不需要Types参数,因此它变成了:
template<typename T>
void unpack(T packed, bool& b1)
{
b1 = packed & 1;
}函数参数名称可能更好。我假设b1只适用于函数的顶层调用。我现在不能决定一个更好的名称,但它应该表明这是当前的位。同样,args应该被命名为remainingBits或类似的东西。
发布于 2014-04-04 18:38:10
我想你知道性病::位集,你有自己的理由去做这件事。您的pack看起来不错,但是unpack要求用户设置特定数量的bool变量,这是不方便的。另一种选择可以是对输出使用std::array:
template<size_t N>
using size = std::integral_constant<size_t, N>;
template<size_t N, typename T>
void unpack(size<N>, array<bool, N>& a, T) { }
template<size_t I, size_t N, typename T>
void unpack(size<I>, array<bool, N>& a, T packed)
{
a[N-I-1] = (packed & (1 << I)) != 0;
unpack(size<I+1>(), a, packed);
}
template<typename T>
std::array<bool, 8*sizeof(T)>
unpack(T packed)
{
std::array<bool, 8*sizeof(T)> a = {};
unpack(size<0>(), a, packed);
return a;
}它可以像这样使用
int val = pack<int>(1, 0, 1);
std::cout << unpack(val) << std::endl;
// prints 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1假设一个简单的流运算符
template<typename S, typename T, size_t N>
S& operator<<(S& s, const std::array<T, N>& a)
{
for (auto i : a)
s << i << " ";
return s;
}这样,您就可以确定输出中没有溢出。
现在,std::vector是输出的另一种选择,它可能受益于其紧凑的std::vector<bool>专门化(当然还有std::bitset )。另一方面,使用std::array可以获得真正的constexpr版本的unpack,而无需递归。pack也可能是非递归的,剩余的constexpr。如果你喜欢这个潜力,我可以详细说明。
constexpr版本实际上,pack很难实现非递归和constexpr,但正如承诺的那样,这里有一个非递归的constexpr版本的unpack (完整的,实例化的):
template <size_t... N>
struct sizes { using type = sizes <N...>; };
template<size_t N, size_t... I, typename T>
constexpr std::array<bool, N>
unpack(sizes<I...>, T packed)
{
return std::array<bool, N>{{(packed & (1 << (N-I-1))) != 0 ...}};
}
template<size_t N, typename T>
constexpr std::array<bool, N>
unpack(T packed)
{
return unpack<N>(typename Range<N>::type(), packed);
}
template<typename T, size_t N = 8*sizeof(T)>
constexpr std::array<bool, N>
unpack(T packed) { return unpack<N>(packed); }其中Range<N>包含序列0,...N-1,并定义为这里。现在您可以这样使用它:
int val = pack<int>(1, 0, 1);
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1
std::cout << unpack(val) << std::endl;
// 0 0 0 0 0 1 0 1
std::cout << unpack<8>(val) << std::endl;第一个版本返回表示给定(整)类型的精度所需的整个数组。第二个允许为数组指定较短的长度;其余(最左边)位被丢弃。
https://codereview.stackexchange.com/questions/46303
复制相似问题