以下片段为类型为T的(假定无符号)整数返回下一个最高2的幂。它通过重复移位来完成此操作。
对于所有意图和目的,在比特移位循环中使用的无符号类型足够大以表示(标称)65536比特数。因此,实际上保持“原样”是可以的。
template <class T>
T supremum_2(T k) {
if (k == T(0)) return T(1);
k--;
for (int i=1; i<sizeof(T)*8; i++) k |= k >> i;
return k+1;
}
要完成专业工作,应在编译时选择循环计数器的类型,以确保能够跨越sizeof(T)* 8而不会溢出。
这可以在编译时使用std :: numeric_traits完成吗?如果是这样的话?
从概念上讲,我希望能够写出如下内容:
typedef unsigned_type_that_can_represent<sizeof(T)*8> counter_type;
...
...
for (counter_type i(1); i<sizeof(T)*8; i<<=1) k = k | k >> i;
...
根据以下讨论,我决定添加以下内容。
换句话说:
我们如何保证在编译时选择有效的(只有它们需要的那么大)和适用于模板代码内部工作的类型?如果我们发现自己在模板代码中使用具体类型,我们可能会通过可能不透明的路径对模板的类型进行无意的假设。
例如,如果我们坚持使用(例如)一个计数器的int,那么一切都会正常工作,直到有人将模板代码与bigint库一起使用。这可以表示具有比int可表示的更多二进制数字的整数。我们是否应该使类型无符号长?当然,这只会延迟问题(尽管很长一段时间)?对于这个解决方案,有“640K - 对每个人都足够大”的阴影或静态数组大小。
在这种情况下,明显但有些低效的选择是将计数器的类型设置为与数字k的类型相同。它(原则上)是低效的,因为我们只要求计数器能够表示对应于k的位数的数字。对于其他情况,这可能是错误的假设。
一般情况怎么样?看起来元编程是一种合适的方法。如何保持这种“理智”?或许,正式地,要求是编译时函数将(可能派生的)抽象类型要求映射到类型。
也许这是YABL(又一个促进图书馆)的工作!
[为漫步道歉]
答案 0 :(得分:1)
我相信你想把你的循环写成
for (int i=1; i<sizeof(T)*8; i++) k |= k >> i;
return k+1;
int至少可以存储最多2^15-1
的值,这对于此来说已经足够了。尽管如此,这是我的工作方式
template<int N, bool B8 = (N>8),
bool B16 = (N>16),
bool B32 = (N>32)>
struct select_t;
template<int N>
struct select_t<N, false, false, false> { typedef unsigned char type; };
template<int N>
struct select_t<N, true, false, false> { typedef unsigned short type; };
template<int N>
struct select_t<N, true, true, false> { typedef unsigned long type; };
int main() { select_t<32>::type i = 0; } // unsigned long
如果您的编译器中碰巧有这种类型,那么您也可以使用unsigned long long
。
答案 1 :(得分:1)
事实上,你是对的。
但实际上,如果你的T是128位,那么最高的移位操作数将是127,整齐地拟合char
......
所以你不是对循环变量类型进行过度设计吗? (可能是由于移位运算符i.s.o.普通增量,正如litb所指出的那样)
答案 2 :(得分:0)
检查static asserts,这可能是解决您问题的方法。
#include <climits>
#include <cwchar>
#include <limits>
#include <boost/static_assert.hpp>
namespace my_conditions {
BOOST_STATIC_ASSERT(std::numeric_limits<int>::digits >= 32);
BOOST_STATIC_ASSERT(WCHAR_MIN >= 0);
} // namespace my_conditions
答案 3 :(得分:0)
您可以使用元编程:
答案 4 :(得分:0)
这可以使用traits实现来完成。像这样:
// base type for template
template <class T>
struct counter_type
{
};
// specialisation for unsigned integer
template <>
struct counter_type <unsigned>
{
typedef unsigned long long value_type; // or whatever
};
template <class T>
T supremum_2(T k) {
if (k == T(0)) return T(1);
k--;
/* Determined by repeated bit shifting. */
for (counter_type<T>::value_type i=1; i<sizeof(T)*8; i<<=1) k = k | k >> i;
return k+1;
}
如果我的语法错误,请原谅我,我总是要查找模板语法。一般的想法就在这里。