我在C ++ 11中从未见过的代码

时间:2013-04-15 17:46:22

标签: c++ c++11 variadic-templates static-assert user-defined-literals

我正在查看此源代码

template<char... digits>
struct conv2bin;

template<char high, char... digits>
struct conv2bin<high, digits...> {
    static_assert(high == '0' || high == '1', "no bin num!");
    static int const value = (high - '0') * (1 << sizeof...(digits)) + 
                             conv2bin<digits...>::value;
};

template<char high>
struct conv2bin<high> {
    static_assert(high == '0' || high == '1', "no bin num!");
    static int const value = (high - '0');
};

template<char... digits>
constexpr int operator "" _b() {
    return conv2bin<digits...>::value;
}

int array[1010_b];

我想知道这是否是有效的C ++。

template<char high, char... digits>
struct conv2bin<high, digits...> {
这是什么?一个不专门的模板专业化?

为什么结构声明里面有代码行,比如

struct conv2bin<high> {
    static_assert(high == '0' || high == '1', "no bin num!");
    static int const value = (high - '0');
};

我很困惑..

2 个答案:

答案 0 :(得分:22)

您的代码显示了三个新的C ++ 11功能:可变参数模板用户定义的文字静态断言

一般可变参数类模板分别指定零个或多个参数,专用版本一个或多个,并且恰好分别为一个。

// digits can be the empty set, so 0 or more arguments
template<char... digits>
struct conv2bin;

// digits can be the empty set, so 1 or more arguments
template<char high, char... digits>
struct conv2bin<high, digits...>

// fully specialized for 1 argument
template<char high>
struct conv2bin<high>

可变参数模板的完整语法有点古怪,Wikipedia上有一篇不错的文章。它对于另一个C ++ 11特性特别有用:完全转发的可变数量的函数参数。

异国情调的int operator "" _b()定义了用户定义的文字,这是一种将自己的单位添加到类型和表达式的方法。它只是意味着_b后跟的整数被标记为某个“单位”。有关详细信息,请参阅此question。一个实际的好处是避免未来的火星着陆器崩溃(其中SI和英制单位在其着陆软件中混合,而编译器无法对其进行诊断)。

static_assert完全符合你的想法:它静态地断言它的条件,即在编译时。断言失败时,编译停止。这是一种尽快检​​测错误的好方法。

<强>更新

当你有部分重叠的参数范围时,可变参数模板的专业化会非常令人惊讶:零或多个参数版本只会匹配示例中的空列表(如果你提供了一个它的定义)。

#include <iostream>

template<int... Args>
struct Test
{
   enum { value = 0 }; 
};

template<int I, int... Args>
struct Test<I, Args...>
{
   enum { value = 2 };
};

template<int I>
struct Test<I>
{
   enum { value = 1 };
};

int main()
{
   std::cout << Test<>::value << "\n";     // matches zero or more version
   std::cout << Test<0>::value << "\n";    // matches single argument version
   std::cout << Test<0, 0>::value << "\n"; // matches one or more version, not the zero or more one!
}

LiveWorkSpace上的输出。

这当然是部分模板专业化的一般规则的一个例子,它表明将选择最专业的版本(一个或多个比零或更多更专业,因为后者总是可以在前者可以使用的地方使用,但反之亦然。但由于可变参数模板通常不会彼此“明显地”不同,因此您应该特别注意它们的部分特化。

答案 1 :(得分:1)

template<char... digits>
struct conv2bin;

这是模板转发声明。它不必完全定义,因为如果以不支持的方式使用它,您将更快地捕获错误(编译将失败)。这个特殊的例子不会导致编译失败,因为专业化涵盖了所有可能的情况。

template<char high, char... digits>
struct conv2bin<high, digits...> {
    static_assert(high == '0' || high == '1', "no bin num!");
    static int const value = (high - '0') * (1 << sizeof...(digits)) + 
                             conv2bin<digits...>::value;
};

这是部分特化,其中设置了一个模板值。其余的只是转发到模板类型的“较低层级”。此模板结构已完全定义,并包含一个int成员变量,该值取决于“高”值和下一个模板。

template<char high>
struct conv2bin<high> {
    static_assert(high == '0' || high == '1', "no bin num!");
    static int const value = (high - '0');
};

再次部分模板专门化,定义模板参数在其列表中只包含一个参数时的值。

所以,总的来说,这是使用可变参数模板的template meta-programming


静态断言用于限制模板变量可以采用的值。