编译时位掩码的递归模板

时间:2012-08-02 13:44:28

标签: c++ metaprogramming bitmask

我尝试使用元编程技术创建编译时位掩码,我的想法是创建这样的东西:

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;

但它会产生大量警告:

  • 警告C4554:&#39;&lt;&lt;&#39; :检查运算符优先级是否存在可能的错误
  • 警告C4293:&#39;&lt;&lt;&#39; :班次计数为负或太大

最后,编译错误:

  • 错误C1202:递归类型或函数依赖关系上下文过于复杂。

所以,我推断出递归性永远不会结束并落入编译器的无限循环中,但我并不理解为什么。

6 个答案:

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