Шаблонные переменные-члены в C++
Часто при написании шаблонного кода мне нужно сохранить экземпляр типа шаблона в переменной-члене. Например, мне может потребоваться кэшировать значение, которое будет использоваться позже. Я хотел бы написать свой код как:
struct Foo
{
template<typename T>
T member;
template<typename T>
void setMember(T value)
{
member<T> = value;
}
template<typename T>
T getMember()
{
return member<T>;
}
};
Где члены являются специализированными по мере их использования. Мой вопрос:
- Возможна ли такая шаблонная переменная-член с текущими средствами генеративного кодирования C ++?
- Если нет, есть ли какие-либо предложения по такой языковой функции?
- Если нет, то есть ли какие-то технические причины, по которым это невозможно?
Должно быть очевидно, что я не хочу перечислять все возможные типы (например, в
std::variant
), поскольку это не является генеративным программированием и было бы невозможно, если бы пользователь библиотеки не совпадал с автором.
Изменить: я думаю, что это несколько отвечает на мой третий вопрос сверху. Причина в том, что сегодняшние компиляторы не могут откладывать создание экземпляров объектов после того, как вся программа была проанализирована: qaru.site/questions/235 / ...
2 ответа
Это возможно в библиотеке путем объединения существующих средств.
Самая простая реализация была бы
std::unordered_map<std::type_index, std::any>
Это несколько неэффективно, поскольку он сохраняет каждый объект дважды (один раз в ключе и один раз внутри каждого), поэтому
std::unordered_set<std::any>
с настраиваемым прозрачным хешем и компаратором будет более эффективным; хотя это было бы больше работы.
Пример .
Как вы говорите, пользователь библиотеки может не совпадать с автором; в частности, деструктор не знает, какие типы были установлены, но он должен найти эти объекты и вызвать их деструкторы, отмечая, что набор используемых типов может отличаться между экземплярами, поэтому эта информация должна храниться в контейнере времени выполнения в пределах
Foo
.
Если вы опасаетесь накладных расходов RTTI, подразумеваемых и, мы можем заменить их эквивалентами более низкого уровня. За
std::type_index
вы можете использовать указатель на
static
создание экземпляра шаблона переменной тега (или любого аналогичного средства), и для
std::any
вы можете использовать стирание шрифта
std::unique_ptr<void, void(*)(void*)>
где удаление - указатель на функцию:
using ErasedPtr = std::unique_ptr<void, void(*)(void*)>;
std::unordered_map<void*, ErasedPtr> member;
struct tag {};
template<class T> inline static tag type_tag;
member.insert_or_assign(&type_tag<T>, ErasedPtr{new T(value), [](void* p) {
delete static_cast<T*>(p);
}});
Пример . Обратите внимание: как только вы сделаете удаление
std::unique_ptr
указатель на функцию, он больше не может быть сконструирован по умолчанию, поэтому мы не можем использовать
operator[]
больше, но должен использовать
insert_or_assign
и
find
. (Опять же, у нас такое же нарушение / неэффективность СУХОГО, поскольку удалитель можно использовать как ключ к карте; использование этого оставлено в качестве упражнения для читателя.)
Возможна ли такая шаблонная переменная-член с текущими средствами генеративного кодирования C ++?
Нет, не совсем то, что вы описываете. Что возможно, так это сделать включающий класс шаблоном и использовать параметры шаблона для описания типов членов класса.
template< typename T >
struct Foo
{
T member;
void setMember(T value)
{
member = value;
}
T getMember()
{
return member;
}
};
В C ++ 14 и более поздних версиях есть шаблоны переменных , но вы не можете сделать шаблон нестатическим членом данных класса.
Если нет, есть ли какие-либо предложения по такой языковой функции?
Не то, что я знаю о.
Если нет, то есть ли какие-то технические причины, по которым это невозможно?
Основная причина в том, что это сделало бы невозможным определение двоичного представления класса. В отличие от шаблонов, класс - это тип, что означает, что его представление должно быть фиксированным, что означает, что в любом месте программы
Foo
и
Foo::member
должны означать одно и то же - одинаковые типы, одинаковые размеры объектов, двоичный макет и так далее. С другой стороны, шаблон не является типом (или, в случае шаблонов переменных, не является объектом). Он становится единым при создании экземпляра , и каждый экземпляр шаблона представляет собой отдельный тип (в случае шаблонов переменных - объект).