我尝试使用元编程技术创建编译时位掩码,我的想法是创建这样的东西:
unsigned int Mask3 = Mask<2>(); // value = 0x03 = b00000000000000000000000000000011
unsigned int Mask3 = Mask<3>(); // value = 0x07 = b00000000000000000000000000000111
unsigned int Mask3 = Mask<7>(); // value = 0x7F = b00000000000000000000000001111111
我尝试的代码就是:
template <const unsigned int N> const unsigned int Mask()
{
if (N <= 1)
{
return 1;
}
else
{
return ((1 << N) | Mask<N - 1>());
}
}
返回1;
但它会产生大量警告:
最后,编译错误:
所以,我推断出递归性永远不会结束并落入编译器的无限循环中,但我并不理解为什么。
答案 0 :(得分:4)
它不需要递归。这应该可以正常工作:
template <const unsigned int N> const unsigned int Mask()
{
return ((1 << N) - 1);
}
它甚至不需要真正成为模板。 (内联)功能没问题。
请注意,如果您想支持N
的任何值,特别是N >= sizeof(unsigned int) * CHAR_BIT
,您可能希望将这些视为特殊情况。
答案 1 :(得分:4)
正如已经指出的那样,你依赖于运行时检查 停止编译时递归,这是行不通的。更重要的是, 或许,对于你想要做的事情,你是在定义一个函数, 在你打电话之前没有任何价值。所以即使你停止了 具有特化的递归,你仍然有一个嵌套的序列 函数,将在运行时调用。
如果要进行完整的编译时评估,则必须定义静态数据 类模板的成员,因为这是编译时间的唯一方法 常量可以出现在模板中。类似的东西:
template <unsigned int N>
struct Mask
{
static unsigned int const value = (1 << (N - 1)) | Mask<N - 1>::value;
};
template <>
struct Mask<0>
{
static unsigned int const value = 0;
};
(我也纠正了你错了的数值。)
当然,你不需要任何复杂的东西。下列 应该做的伎俩:
template <unsigned int N>
struct Mask
{
static unsigned int const value = (1 << (N + 1)) - 1;
};
template <>
struct Mask<0>
{
static unsigned int const value = 0;
};
(您仍然需要0的专业化。否则,0表示所有位 集。)
最后,当然:要访问该值,您需要编写一些东西
比如Mask<3>::value
。 (您可能希望将其包装在宏中。)
答案 2 :(得分:3)
在编译时创建模板,但您依靠运行时行为来停止递归。
例如,如果你实例化Mask&lt; 2&gt;,它将使用Mask&lt; 1&gt;,它将使用Mask&lt; 0&gt;,它将使用Mask&lt; -1&gt;等。
你有一个运行时检查N是&lt; = 1,但这在编译时没有帮助。它仍然创造了无限的函数序列。
答案 3 :(得分:3)
要钝化模板实例化递归,您需要引入一个显式特化:
template <0> const unsigned int Mask()
{
return 1;
}
您的递归永远不会结束,因为编译器会尝试为两个if分支生成模板实现。因此,当它生成掩码&lt; 0&gt;时,它还生成Mask&lt; 0xffffffff&gt;等等
答案 4 :(得分:1)
到目前为止,答案仅解决了第二个错误(C1202),但您提出的要求不止于此。
警告C4554是由涉及模板参数的Microsoft编译器错误和&lt;&lt;运营商。因此,(1 <&lt; N)生成警告。如果N是普通参数,那么当然不会有警告。
非常简单的解决方法是使用(1&lt;&lt;(N))而不是(1&lt;&lt;&lt; N),C4554消失!
答案 5 :(得分:0)
C ++ 11 - 没有递归或模板:
constexpr unsigned mask(unsigned N) { return unsigned(~(-1<<N)); }