Есть ли шаблон, который может генерировать статические / динамически связанные версии класса?
Я работаю над некоторым библиотечным кодом и хочу, чтобы пользователи могли использовать преимущества статического связывания, если они могут. Если они не могут создать экземпляр класса во время компиляции, я хочу, чтобы была динамическая версия класса, чтобы его можно было создавать во время выполнения.
Для быстрого примера, скажем, у меня есть шаблон структуры 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) {}
};
Есть ли название для этого шаблона / проблемы? Есть ли библиотека метапрограммирования, которая имеет шаблоны, которые могут генерировать оба определения для меня? Как мне избежать необходимости писать определения моих классов дважды?
1 ответ
Существует простой механизм, позволяющий помещать части, которые не зависят от проблемы динамического / статического значения, в одно место: поместить их в другой класс, назовем его basic_A
и давайте назовем контейнер статических / динамических значений, который вы показываете в вопросе value_A
, Есть разные способы подключения value_A
а также basic_A
сформировать полный A
учебный класс:
Агрегация
basic_A
внутриvalue_A
, Это будет означать, что вы должны направить каждый методbasic_A
черезvalue_A
и предоставить соответствующие однострочники в обеих специализацияхvalue_A
, Это, вероятно, не слишком выгодно, потому что вы должны продублировать все однострочники, так что поцарапайте это.Агрегация
value_A
внутриbasic_A
, Вы должны были бы либо сделатьbasic_A
шаблон также только для передачи параметровvalue_A
и предоставить правильные конструкторы для обеих специализаций, возможно, как-то отключив и включив их через SFINAE. Не очень красивый и обслуживаемый кусок кода. Альтернативой может быть создание общего базового класса (интерфейса) для двух специализацийvalue_A
естьunique_ptr
к этому интерфейсу вbasic_A
и передать готовый построенныйvalue_A
вbasic_A
конструктор, за счет вызова виртуальной функции и перенаправления poitner всякий раз, когда вы хотите получить доступ к значению. Фу, особенно еслиA
предназначен для небольшого и быстрого легкого класса.унаследовать
basic_A
отvalue_A
, Те же проблемы, что и в 2., применяются в отношении конструкторов и пересылки параметров шаблона.унаследовать
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; }
};