Шаблонные переменные-члены в 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>;
    }
};

Где члены являются специализированными по мере их использования. Мой вопрос:

  1. Возможна ли такая шаблонная переменная-член с текущими средствами генеративного кодирования C ++?
  2. Если нет, есть ли какие-либо предложения по такой языковой функции?
  3. Если нет, то есть ли какие-то технические причины, по которым это невозможно?

Должно быть очевидно, что я не хочу перечислять все возможные типы (например, в 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должны означать одно и то же - одинаковые типы, одинаковые размеры объектов, двоичный макет и так далее. С другой стороны, шаблон не является типом (или, в случае шаблонов переменных, не является объектом). Он становится единым при создании экземпляра , и каждый экземпляр шаблона представляет собой отдельный тип (в случае шаблонов переменных - объект).

Другие вопросы по тегам