以位为单位的积分类型大小的元程序

时间:2012-10-01 05:17:16

标签: c++ metaprogramming

我通过解决各种玩具问题熟悉元编程。这是让我难倒的一个:如何编写一个元数据,以位为单位提供整数类型参数的大小。特别是,我想在没有CHAR_BIT或任何其他幻数的情况下这样做。

我开始使用以下非元程序:

template <typename T>
int sizeInBits(void) {
    T flag = 1;
    int count = 0;
    while (flag != 0) {
        flag <<= 1;
        ++count;
    }
    return count;
}

转换为元程序,我认为它看起来像这样:

template<typename T, int COUNT = 0, T FLAG = 1>
struct SizeInBits {
    enum {Result = SizeInBits<T, COUNT + 1, FLAG << 1 >::Result};
};

template<typename T, int COUNT>
struct SizeInBits<T, COUNT, 0> {
    enum {Result = COUNT};
};

但是,此程序无法编译,因为apparently专门处理具有模板参数类型的非类型模板参数是非法的,因此我使用gcc 4.6获得以下错误消息:

error: type ‘T’ of template argument ‘0’ depends on a template parameter

有什么想法吗?

修改
我基本上在寻找<climits><limits>中魔术数字等效的元程序。因此,举例来说,我希望SizeInBits<char>::Result在我的系统上提供8,并SizeInBits<unsigned>::Result提供32

关于签名类型的左按位移位运算符的有效性的注意事项
在注释中,有人担心在发生溢出时是否为已签名类型定义了FLAG << 1。根据{{​​3}}中引用的C ++ 03标准,它被定义并执行上述算法所期望的内容。

3 个答案:

答案 0 :(得分:2)

非类型模板参数的类型不能依赖于部分特化的模板参数。编译器不允许以下部分特化:

template <class T, T t> class X { };

// Invalid partial specialization
template <class T> class X<T, 25> { };

要解决此问题,您只需使用int FLAG

即可
#include <iostream> 

template<typename T, int COUNT = 0, int FLAG = 1>
struct SizeInBits {
    enum { 
        Result = SizeInBits<T, COUNT + 1, FLAG << 1>::Result
    };
};

template<typename T, int COUNT>
struct SizeInBits<T, COUNT, 0> {
    enum {
        Result = COUNT
    };
};

int main() {
   std::cout << SizeInBits<int, 10>::Result << std::endl;
}

但似乎你需要sizeof(T) * CHAR_BIT或者像这样:

#include <iostream> 
#include <climits>

template<typename T>
struct SizeInBits {
    enum { 
        Result = sizeof(T) * CHAR_BIT
    };
};

int main() {
   std::cout << SizeInBits<long>::Result << std::endl;
}

答案 1 :(得分:2)

这个怎么样:

template<typename T, int COUNT = 0, int FLAG = 1>
struct SizeInBits {
    enum {Result = SizeInBits<T, COUNT + 1, static_cast<T>(FLAG) << 1 >::Result};
};

template<typename T, int COUNT>
struct SizeInBits<T, COUNT, 0> {
    enum {Result = COUNT};
};

它对我有用。

答案 2 :(得分:2)

这应该可以解决问题:

template<typename T, unsigned long long flag = 1uLL>
struct bits_in {
    enum {value = 1 + bits_in<T, (unsigned long long)(T)(flag << 1)>::value};
};

template<typename T>
struct bits_in<T, 0uLL> {
    enum {value = 0};
};

Nicolas's version不同,它更简单,不会产生任何警告,actually works correctly