Есть ли шаблон, который может генерировать статические / динамически связанные версии класса?

Я работаю над некоторым библиотечным кодом и хочу, чтобы пользователи могли использовать преимущества статического связывания, если они могут. Если они не могут создать экземпляр класса во время компиляции, я хочу, чтобы была динамическая версия класса, чтобы его можно было создавать во время выполнения.

Для быстрого примера, скажем, у меня есть шаблон структуры A:

template<bool dynamic, int value=0> struct A
{
static const int Value = value;
};template<> struct A<true>
{
int Value;
A(int value) : Value(value) {}
};

Эти определения позволяют пользователям библиотеки создавать A статически и динамически:

A<true> dynamicA = A<true>(5);

A<false, 5> staticA;

Проблема с этим методом заключается в том, что я должен написать определение класса дважды. Я могу придумать несколько способов реализации шаблона, который будет генерировать обе версии самостоятельно, но я вижу, что это становится большой работой. Особенно для классов, которые будут использовать различное количество параметров, например:

// It would be much harder to generate a static version of this class,
// though it is possible with type lists.  Also, the way I'm imagining it,
// the resulting classes probably wouldn't be very easy to use.
struct A
{
vector<int> Values;
A(vector<int> value) : Values(value) {}
};

Есть ли название для этого шаблона / проблемы? Есть ли библиотека метапрограммирования, которая имеет шаблоны, которые могут генерировать оба определения для меня? Как мне избежать необходимости писать определения моих классов дважды?

3

Решение

Существует простой механизм, позволяющий помещать части, которые не зависят от проблемы динамического / статического значения, в одно место: поместить их в другой класс, назовем его basic_A, и давайте назовем контейнер статических / динамических значений, который вы показываете в вопросе value_A, Есть разные способы подключения value_A а также basic_A сформировать полный A учебный класс:

  1. Агрегация basic_A внутри value_A, Это будет означать, что вы должны направить каждый метод basic_A через value_A и предоставить соответствующие однострочники в обеих специализациях value_A, Это, вероятно, не слишком выгодно, потому что вы должны продублировать все однострочники, так что поцарапайте это.

  2. Агрегация value_A внутри basic_A, Вы должны были бы либо сделать basic_A шаблон также только для передачи параметров value_A и предоставить правильные конструкторы для обеих специализаций, возможно, как-то отключив и включив их через SFINAE. Не очень красивый и обслуживаемый кусок кода. Альтернативой может быть создание общего базового класса (интерфейса) для двух специализаций value_Aесть unique_ptr к этому интерфейсу в basic_A и передать готовый построенный value_A в basic_Aконструктор, за счет вызова виртуальной функции и перенаправления poitner всякий раз, когда вы хотите получить доступ к значению. Фу, особенно если A предназначен для небольшого и быстрого легкого класса.

  3. унаследовать basic_A от value_A, Те же проблемы, что и в 2., применяются в отношении конструкторов и пересылки параметров шаблона.

  4. унаследовать value_A от basic_A, Проблема строительства исчезает, но сейчас basic_A не может легко получить доступ value_Aценность. Одним из решений было бы иметь чисто виртуальную функцию getValue() в basic_A которые две специализации value_A должны реализовать. Это опять-таки имеет стоимость отправки виртуальной функции, которая может быть нежелательна для небольшого легкого класса, но она позволяет инкапсуляцию, так как basic_A не является шаблоном и может скрывать свою реализацию в файле .cpp. Другой подход заключается в использовании полиморфизма во время компиляции через CRTP, который сделает basic_A шаблон снова.

Вот два примера для двух подходов из 4:

4а: getValue() как виртуальная функция:

//basic_a.hpp

struct basic_A {
int foo() const;
virtual int getValue() const = 0;
};

//basic_A.cpp
int basic_A::foo() const { return 10 * getValue(); }

4b: getValue() через CRTP

template <class Value_t>
struct basic_A {
int foo() const { return 10 * value_(); }
private:
int value_() const { return static_cast<Value_t const&>(*this).getValue(); }
};

Шаблон A ака. A_value за 4б следует. Для 4а это почти то же самое, просто потерять аргументы шаблона и скобки из basic_A так как это простой класс:

template <bool dyn, int value = 0>
struct A;

template <>
struct A<true, 0> : basic_A<A<true, 0>>
{
int val;
int getValue() const { return val; }
};

template <int value>
struct A<false, value> : basic_A<A<false,value>>
{
int geValue() { return value; }
};
3

Другие решения


По вопросам рекламы [email protected]