2012-08-02 90 views
1

我试图用metaprograming技术来创建一个编译时位掩码递归模板,我的想法是创建这样的:了编译时位掩码

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>()); 
    } 
} 

return 1;

但它导致吨双警告:

  • 警告C4554: '< <':检查运算符优先级为可能出现的错误
  • 警告C4293: '< <':移位计数负或过大

而在最后,编译错误:

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

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

+0

为什么你不会像“int result = 0; for(i = 0; i SinisterMJ 2012-08-02 13:50:26

回答

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。 (您可能希望在宏这个包起来。)

+0

作为被接受的答案,仅仅是因为解释了如何在temeplate形式中公平而明确地做出解释,但最好的答案是Sander De Dycker的答案。 – 2012-08-02 15:02:23

+2

除了来自Sander De Dycker的一个仍然涉及函数调用,所以值不是一个常量(然而,对于C++ 11,它可以被声明为'constexpr'。)并且它失败了'N == 0'(否则,它对应于我的第二个解决方案,如上所示)。 – 2012-08-02 15:14:48

+0

如果N> = 32,我的编译器抱怨说,它不符合标准,即使我把它做成'unsigned long long'。命令:**'clang ++ - 3.3 -Wall -Werror -std = C++ 11 -pedantic Mask.cpp ** (严格的C++ 11的标志标准)结果:'Mask.cpp:7:48:错误:静态数据的类内初始化器成员不是一个常量表达;将它折叠成常量是GNU扩展[-Werror,-Wgnu]'**'static unsigned long long const value =(1 <<(N + 1)) - 1; ** ** Mask.cpp:19: 10:注意:在模板类'Mask <32>'在此处请求的实例化中**'cout << Mask<32> :: value << endl;'** – 2014-03-05 09:56:57

3

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

例如,如果你实例面膜< 2>,它是将要使用面膜< 1>,这是将要使用面膜< 0>,这是将要使用面膜< -1>等

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

3

钝模板实例递归您需要引入一个明确的分工:

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

你的递归永远不会结束,因为编译器试图生成模板实现两个如果分支。所以当它生成Mask时它也会生成Mask < 0xffffffff等等

4

它不需要递归。这应该工作得很好:

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

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

请注意,如果您要支持N的任何值,特别是N >= sizeof(unsigned int) * CHAR_BIT,那么您可能希望将这些值作为特例处理。

0

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

constexpr unsigned mask(unsigned N) { return unsigned(~(-1<<N)); } 
1

到目前为止,答案只回答了第二个错误(C1202),但你问比那更多的。

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

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