C ++ - 通用编程 - 类型选择

时间:2009-06-24 09:00:28

标签: c++ templates types generic-programming

以下片段为类型为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(又一个促进图书馆)的工作!

[为漫步道歉]

5 个答案:

答案 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;
}

如果我的语法错误,请原谅我,我总是要查找模板语法。一般的想法就在这里。