Страницы

Поиск по вопросам

понедельник, 9 декабря 2019 г.

Узнать тип на этапе компиляции

#cpp


Можно ли определить тип переданных параметров variadic templates, на этапе компиляции?

Хочу использовать static_assert для проверки допустимости передачи параметра в шаблон.

Допустим запретить передачу в шаблон типа float

int main() {

    MyClass  obj; // так можно
    MyClass  obj;          // так тоже
    MyClass  obj;            // а вот так нельзя

    return 0;
} 

    


Ответы

Ответ 1



Как уже ответил @AnT, в C++17 появились fold expressions. Если использование C++17 недоступно, то можно сделать следующим образом: template struct any_of; template struct any_of: any_of {}; template struct any_of: std::true_type {}; template struct any_of: std::false_type {}; //использование template struct MyClass { static_assert(!any_of::value, "float not supported"); }; //... MyClass d1;//ok MyClass d2;//assertion Для понимания работы этого кода предлагаю разобраться сначала с возможной реализацией is_same для двух типов: //Общая версия шаблона наследуется от std::false_type, //что дает сразу готовый член value и операцию преобразования к bool. //false_type::value имеет значение false template struct my_is_same: std::false_type {}; //Весь "фокус" в данной специализации. //Данная специализация будет выбрана, если типы переданные в шаблон одинаковые (T и T). //И эта специализация наследуется от std::true_type, ///и член value равен true template struct my_is_same: std::true_type {}; //... static_assert(my_is_same::value, "failure"); //ок - my_is_same здесь наследник std::true_type static_assert(my_is_same::value, "failure"); //ошибка - my_is_same здесь наследник std::false_type Думаю, прием с наследованием от false_type и true_type понятен. В начальном коде используется еще один прием - наследование в шаблоне класса от самого себя с другим набором аргументов шаблона. Разберем и его на примере первоначального кода. //Общая версия шаблона не реализуется и не используется //Вместо нее будут использоваться специализации template struct any_of; //Данная специализация выбирается если параметров более одного. //any_of в данном случае наследуется от any_of, при этом отсекается параметр HeadType, //т.е. он не передается в качестве аргумента шаблону базового класса. //Таким образом с каждой "ступенью" наследования отсекается один тип из списка аргументов. //при этом отсекается второй тип (HeadType), т.к. первый (CheckType) - //это тот тип, который необходимо проверить. template struct any_of: any_of {}; //Наследование и "отсечение" будет происходить до тех пор, //пока не упремся в одну из следующих специализаций: //Эта специализация будет выбрана, если первый и второй параметры шаблона одинаковы. //Здесь уже используется наследование от std::true_type, //что прерывает цепочку наследования от any_of, //таким образом any_of становится наследником std::true_type, //если хоть один аргументов, переданных изначально в any_of, совпадает с первым аргументом. template struct any_of: std::true_type {}; //если же мы дошли до такого момента, когда остался только проверяемый тип, //значит в списке аргументов any_of не было типов, совпадающих с первым, //поэтому прерываем наследование от any_of наследованием от std::false_type. template struct any_of: std::false_type {}; //Таким образом, если в пакете Args... в any_of есть тип Type, //то any_of станет наследником std::true_type, //в ином случае any_of станет наследником false_type static_assert(any_of::value, "float not supported");//ok - any_of наследник std::true_type static_assert(any_of::value, "float not supported");//ошибка - any_of наследник std::false_type

Ответ 2



Весь template type deduction работает именно на этапе компиляции. То есть, когда вы определяете объект шаблонного класса, компилятор скомпилирует используемый класс именно с подставленным типом в шаблонный параметр. Если вы по каким-то причинам хотите запретить пользователю использовать в качестве параметра какой-то определенный тип, то вы должны запретить конструктор для данного шаблона с таким параметром (типом). Пример : template class Foo { public: T a; }; template class Foo { public: Foo() = delete; }; Если вы попытаетесь создать объект Foo, то компилятор выдаст ошибку. Отвечая на вторую часть вопроса про static_assert. В данном случае можно попробовать определить тип именно так static_assert(std::is_same::value, "retval must be bool"); Принцип использования должен быть понятен

Ответ 3



Для этого фактически и предназначены fold expressions в C++17 #include template class MyClass { static_assert(!(... || std::is_same_v)); };

Комментариев нет:

Отправить комментарий