我通过解决各种玩具问题熟悉元编程。这是让我难倒的一个:如何编写一个元数据,以位为单位提供整数类型参数的大小。特别是,我想在没有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标准,它被定义并执行上述算法所期望的内容。
答案 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。