1

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

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:“<<”:检查运算符优先级是否存在可能的错误
  • 警告 C4293:“<<”:移位计数为负数或太大

最后,编译错误:

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

所以,我推断递归永远不会结束并陷入编译器无限循环,但我不明白为什么。

4

6 回答 6

4

它不需要是递归的。这应该可以正常工作:

template <const unsigned int N> const unsigned int Mask()
{
    return ((1 << N) - 1);
}

它甚至不需要真的是一个模板。(内联)函数是可以的。

请注意,如果您想支持 的任何值N,特别是N >= sizeof(unsigned int) * CHAR_BIT,您可能希望将其视为特殊情况。

于 2012-08-02T13:57:40.910 回答
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. (您可能希望将其包装在宏中。)

于 2012-08-02T14:14:19.960 回答
3

模板是在编译时创建的,但您依赖运行时行为来停止递归。

例如,如果你实例化 Mask<2>,它将使用 Mask<1>,它将使用 Mask<0>,它将使用 Mask<-1>,等等。

您有一个运行时检查 N <= 1,但这在编译时没有帮助。它仍然创建了无限的函数序列。

于 2012-08-02T13:54:59.920 回答
3

要钝化模板实例化递归,您需要引入一种显式特化:

template <0> const unsigned int Mask()
{
    return 1;
}

您的递归永远不会结束,因为编译器会尝试为两个 if 分支生成模板实现。因此,当它生成 Mask<0> 时,它也会生成 Mask<0xffffffff> 等等

于 2012-08-02T13:56:55.770 回答
1

C++11——没有递归或模板:

constexpr unsigned mask(unsigned N) { return unsigned(~(-1<<N)); }
于 2012-08-23T12:57:33.173 回答
1

到目前为止,答案只解决了第二个错误(C1202),但你问的不止这些。

警告 C4554 是由涉及模板参数和 << 运算符的 Microsoft 编译器错误引起的。因此, (1 << N) 会生成警告。如果 N 是一个普通参数,当然不会有警告。

非常简单的解决方法是使用 (1 << (N)) 而不是 (1 << N),C4554 消失了!

于 2015-11-13T03:00:23.010 回答