#c #templates #template-meta-programming
#c #c 20
Вопрос:
Допустим, у меня есть это:
template<typename T>
class my_template {...};
Ожидается, что теперь пользователи смогут специализироваться my_template
на своих собственных типах. И они передадут эти типы некоторым моим функциям API, которые будут использовать свойства my_template<T>
для выполнения чего-либо.
Итак, в какой-то момент в моем коде у меня есть my_template<T>
. Мне нужна какая-то метафункция, которая принимает my_template<T>
и выдает значение времени компиляции true
, если my_template<T>
это предоставленная пользователем специализация (частичная или явная), и false
если это не так.
Наиболее очевидным решением является добавление псевдонима частного члена или какого-либо другого concept
обнаруживаемого частного объявления в основное my_template
определение и полагаться на то, что оно отсутствует в пользовательских специализациях. Однако пользователь может создать явную специализацию, предоставив соответствующее определение. Так что это не является надежным.
Этот вопрос не софистика. Спецификация C 20 имеет метафункцию ITER_CONCEPT(I)
, которая имеет различное внутреннее поведение в зависимости от того, является ли std::iterator_traits<I>
тип специализацией из основного шаблона или специализации, предоставленной пользователем. Конечно, будучи стандартной библиотекой, они могут создать идентификатор с префиксом в __
качестве члена основного шаблона и, таким образом, объявить любую попытку подделки из пользовательского пространства неопределенным поведением.
Это то, что может сделать только реализация компилятора / стандартной библиотеки с защитой от ошибок, или это возможно сделать в C 20?
Ответ №1:
Наиболее очевидным решением является добавление псевдонима частного члена или какого-либо другого частного объявления, определяемого концепцией, в основное определение my_template и полагаться на то, что его нет в пользовательских специализациях. Однако пользователь может создать явную специализацию, предоставив соответствующее определение. Так что это не является надежным.
Это в основном так, да. Например, свойства итератора libstdc имеют свой шаблон основного класса, наследуемый от шаблона скрытого базового класса, а затем проверяет наследование от этой базы.
Да, пользователь может создать явную специализацию, предоставив соответствующее определение, но, например, не делайте этого. Это не то, что вы сделали бы случайно, это явно и бессмысленно вредоносно, и типичная поговорка гласит, что библиотека и язык защищают от Мерфи, а не от Макиавелли.
С помощью модулей вы можете еще больше затруднить пользователю явное вредоносное по, экспортируя основной шаблон, но фактически не экспортируя базовый класс, который вы используете для проверки, был ли шаблон класса специализированным:
export module std;
namespace std {
template <typename T> struct __iterator_traits { };
template <typename T>
export struct iterator_traits : __iterator_traits { };
}
Теперь пользователь не может даже назвать имя std::__iterator_traits
, чтобы взломать библиотеку. Возможно, отражение все равно даст им способ сделать это (при условии, что эти вещи все еще доступны), но теперь они действительно будут прыгать через множество обручей.
Говоря об отражении, часть указанного в настоящее время API (P1240) включает функции is_specialization
, is_partial_specialization
, и is_explicit_specialization
. Если они означают то, что я думаю, что они означают, то, когда мы в конечном итоге получим отражение, тогда библиотеке не пришлось бы использовать эти взломы magic base / magic member и она могла бы просто напрямую проверить, была ли предоставленная специализация частичной или явной специализацией или нет. Я полагаю, что библиотеку, вероятно, следует добавить is_primary_specialization
для полноты.