Код, который я никогда не видел в С++ 11

Я смотрю на этот исходный код

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];

и мне интересно, действительно ли это С++.

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

Я запутался..

Ответы

Ответ 1

В вашем коде показаны три новые возможности С++ 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 содержит достойную статью. Это особенно полезно для другой функции С++ 11: совершенная переадресация вариационного числа аргументов функции.

Экзотический выглядящий int operator "" _b() определяет определяемый пользователем литерал, который является способом добавления ваших собственных единиц к вашим типам и выражению. Это просто означает, что целые числа, за которыми следует _b, помечены определенной "единицей". Дополнительную информацию см. В этом вопросе . Одно практическое преимущество было бы в том, чтобы избежать будущих сбоев приземления на Марсе (где СИ и имперские подразделения были смешаны в их программном обеспечении для посадки, без возможности компилятора его диагностировать).

static_assert выполняет именно то, что, по вашему мнению, делает: он статически ставит свое состояние, то есть в время компиляции. Когда утверждение не выполняется, компиляция прекращается. Это отличный способ обнаружить ошибки как можно скорее.

UPDATE

Специализация вариационных шаблонов может быть очень удивительной, если у вас есть частично перекрывающиеся диапазоны аргументов: версия с нулевыми или более аргументами будет соответствовать только пустым спискам в вашем примере (в случае, если вы предоставили определение для него).

#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 member, значение которой зависит от "высокого" значения и следующего шаблона.

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

Опять-таки частичная спецификация шаблона, определяющая значение, когда параметры шаблона содержат только один параметр в этом списке.

Итак, в общей сложности это мета-программирование шаблонов с использованием вариативных шаблонов.


Статические утверждения существуют, чтобы ограничить значения, которые могут принимать значения переменных.