【问题标题】:Code I've never seen in C++11我在 C++11 中从未见过的代码
【发布时间】:2013-04-07 22:35:04
【问题描述】:

我在看这个源代码

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

我很困惑..

【问题讨论】:

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


【解决方案1】:

您的代码展示了三个新的 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。一个实际的好处是避免未来的火星着陆器崩溃(国际单位制和英制单位在他们的着陆软件中混合在一起,而编译器无法诊断它)。

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 上的输出。

这当然是部分模板特化的一般规则的一个例子,它指出将选择最特化的版本(一个或多个比零个或多个更特化,因为后者总是可以在前者可以使用的地方使用,但反之则不然)。但由于可变参数模板通常彼此之间没有那么“明显”的不同,因此您应该格外小心它们的部分特化。

【讨论】:

    【解决方案2】:
    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成员变量,它的值取决于'high'值和下一个模板。

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

    再次部分模板特化,定义模板参数列表中仅包含一个参数时的值。

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


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

    【讨论】:

      猜你喜欢
      • 2021-09-15
      • 1970-01-01
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      • 2020-03-30
      • 2013-04-06
      • 1970-01-01
      • 2014-09-01
      相关资源
      最近更新 更多