В коде, содержащем шаблоны, зачастую необходимо по-разному выполнять определенные действия в зависимости от типа, для которого конкретный шаблон был специализирован. В С++17 появились выражения constexpr-if
, позволяющие
Как это делается
В этом примере мы реализуем небольшой вспомогательный шаблонный класс. Он может работать с разными типами, поскольку способен выбирать различные пути выполнения кода в зависимости от типа, для которого мы конкретизируем шаблон.
1. Напишем обобщенную часть кода. В нашем примере рассматривается простой класс, который добавляет значение типа U
к элементу типа T
с помощью функции add
:
template
class addable
{
T val;
public:
addable(T v) : val{v} {}
template
T add(U x) const {
return val + x;
}
};
2. Представим, что тип T
— это std::vector<что-то>
, а тип U
— просто int
. Каков смысл выражения «добавить целое число к вектору»? Допустим, нужно добавить данное число к каждому элементу вектора. Это делается в цикле:
template
T add(U x)
{
auto copy (val); // Получаем копию элемента вектора
for (auto &n : copy) {
n += x;
}
return copy;
}
3. Следующий и последний шаг заключается в том, чтобы T
— это вектор, состоящий из элементов типа U
, то выполняем
template
T add(U x) const {
if constexpr (std::is_same_v
auto copy (val);
for (auto &n : copy) {
n += x;
}
return copy;
} else {
return val + x;
}
}
4. Теперь класс можно использовать. Посмотрим, насколько хорошо он может работать с разными типами, такими как int
, float
, std::vector
и std::vector
:
addable
addable
addable
std::vector
addable
// is std::vector
std::vector
addable
// is {"az", "bz", "cz"}
Как это работает
Новая конструкция constexpr-if
работает точно так же, как и обычные конструкции if-else
. Разница между ними заключается в том, что значение условного выражения определяется constexpr-if
. Кто-то может сказать, что эти механизмы работают так же, как и макросы препроцессора #if
и #else
, предназначенные для подстановки текста, но в данном случае всему коду даже не нужно быть синтаксически правильным. Ветвления конструкции constexpr-if
должны быть
Чтобы определить, должен ли код добавлять значение х
к вектору, задействуем типаж std::is_same
. Выражение std::is_same::value
вычисляется в логическое значение true, если A и B имеют один и тот же тип. В нашем примере применяется условие std::is_same
, которое имеет значение true
, если пользователь конкретизировал шаблон для класса T = std::vector
и пробует вызвать функцию add
с параметром типа U = X
.
В одном блоке constexpr-if-else
может оказаться несколько условий (обратите внимание, что a
и b
должны зависеть от параметров шаблона, а не только от констант времени компиляции):
if constexpr (a) {
// что-нибудь делаем
} else if constexpr (b) {
// делаем что-нибудь еще
} else {
// делаем нечто совсем другое
}
С помощью C++17 гораздо легче как выразить, так и прочитать код, получающийся при метапрограммировании.
Дополнительная информация
Для того чтобы убедиться, каким прекрасным новшеством являются конструкции constexpr-if
для C++, взглянем, как решалась та же самая задача до С++17:
template
class addable
{
T val;
public:
addable(T v) : val{v} {} template
std::enable_if_t>::value, T>
add(U x) const { return val + x; }
template