Можно ли определить тип переданных параметров variadic templates
, на этапе компиляции?
Хочу использовать static_assert
для проверки допустимости передачи параметра в шаблон.
Допустим запретить передачу в шаблон типа float
int main() {
MyClass <int, double, int, char> obj; // так можно
MyClass <double, char> obj; // так тоже
MyClass <int, float> obj; // а вот так нельзя
return 0;
}
Как уже ответил @AnT, в C++17 появились fold expressions
.
Если использование C++17 недоступно, то можно сделать следующим образом:
template<typename ... Args>
struct any_of;
template<typename CheckType, typename HeadType, typename ... Args>
struct any_of<CheckType, HeadType, Args...>: any_of<CheckType, Args...> {};
template<typename CheckType, typename ... Args>
struct any_of<CheckType, CheckType, Args...>: std::true_type {};
template<typename CheckType>
struct any_of<CheckType>: std::false_type {};
//использование
template<typename ... Args>
struct MyClass
{
static_assert(!any_of<float, Args...>::value, "float not supported");
};
//...
MyClass<int, double> d1;//ok
MyClass<float, int> d2;//assertion
Для понимания работы этого кода предлагаю разобраться сначала с возможной реализацией is_same
для двух типов:
//Общая версия шаблона наследуется от std::false_type,
//что дает сразу готовый член value и операцию преобразования к bool.
//false_type::value имеет значение false
template<typename T, typename U>
struct my_is_same: std::false_type
{};
//Весь "фокус" в данной специализации.
//Данная специализация будет выбрана, если типы переданные в шаблон одинаковые (T и T).
//И эта специализация наследуется от std::true_type,
///и член value равен true
template<typename T>
struct my_is_same<T, T>: std::true_type
{};
//...
static_assert(my_is_same<float, float>::value, "failure"); //ок - my_is_same здесь наследник std::true_type
static_assert(my_is_same<float, int>::value, "failure"); //ошибка - my_is_same здесь наследник std::false_type
Думаю, прием с наследованием от false_type
и true_type
понятен.
В начальном коде используется еще один прием - наследование в шаблоне класса от самого себя с другим набором аргументов шаблона. Разберем и его на примере первоначального кода.
//Общая версия шаблона не реализуется и не используется
//Вместо нее будут использоваться специализации
template<typename ... Args>
struct any_of;
//Данная специализация выбирается если параметров более одного.
//any_of в данном случае наследуется от any_of, при этом отсекается параметр HeadType,
//т.е. он не передается в качестве аргумента шаблону базового класса.
//Таким образом с каждой "ступенью" наследования отсекается один тип из списка аргументов.
//при этом отсекается второй тип (HeadType), т.к. первый (CheckType) -
//это тот тип, который необходимо проверить.
template<typename CheckType, typename HeadType, typename ... Args>
struct any_of<CheckType, HeadType, Args...>: any_of<CheckType, Args...> {};
//Наследование и "отсечение" будет происходить до тех пор,
//пока не упремся в одну из следующих специализаций:
//Эта специализация будет выбрана, если первый и второй параметры шаблона одинаковы.
//Здесь уже используется наследование от std::true_type,
//что прерывает цепочку наследования от any_of,
//таким образом any_of становится наследником std::true_type,
//если хоть один аргументов, переданных изначально в any_of, совпадает с первым аргументом.
template<typename CheckType, typename ... Args>
struct any_of<CheckType, CheckType, Args...>: std::true_type {};
//если же мы дошли до такого момента, когда остался только проверяемый тип,
//значит в списке аргументов any_of не было типов, совпадающих с первым,
//поэтому прерываем наследование от any_of наследованием от std::false_type.
template<typename CheckType>
struct any_of<CheckType>: std::false_type {};
//Таким образом, если в пакете Args... в any_of<Type, Args...> есть тип Type,
//то any_of<Type, Args...> станет наследником std::true_type,
//в ином случае any_of<Type, Args...> станет наследником false_type
static_assert(any_of<float, int, float>::value, "float not supported");//ok - any_of наследник std::true_type
static_assert(any_of<float, int, double>::value, "float not supported");//ошибка - any_of наследник std::false_type
Весь template type deduction работает именно на этапе компиляции. То есть, когда вы определяете объект шаблонного класса, компилятор скомпилирует используемый класс именно с подставленным типом в шаблонный параметр. Если вы по каким-то причинам хотите запретить пользователю использовать в качестве параметра какой-то определенный тип, то вы должны запретить конструктор для данного шаблона с таким параметром (типом). Пример :
template <typename T>
class Foo
{
public:
T a;
};
template<typename int>
class Foo
{
public:
Foo() = delete;
};
Если вы попытаетесь создать объект Foo<int>
, то компилятор выдаст ошибку. Отвечая на вторую часть вопроса про static_assert. В данном случае можно попробовать определить тип именно так
static_assert(std::is_same<decltype(foo.a), bool>::value, "retval must be bool");
Принцип использования должен быть понятен
Для этого фактически и предназначены fold expressions в C++17
#include <type_traits>
template <typename... Args>
class MyClass
{
static_assert(!(... || std::is_same_v<Args, float>));
};
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Как в QSpinBox, QDoubleSpinBox задать некоторый множественный диапазон? Примеры как должен работать этот SpinBox, аналогично для DoubleSpinBox: 1) Должен принимать...
Столкнулся с проблемой, но на существующих топиках об этой проблеме не нашел решенияЯ в затруднении, все include'ы правильно расставлены вроде,...
у меня есть std::set с кастомным компаратором, в set я кладу свой тип данных, который содержит два параметра, уникальность должна обеспечиваться...
Начал изучать Java и Spring frameworkДелаю задание, часть которого - хранение набора точек с привязкой к какой-то площади (не важно к какой, к вопросу...