2014-11-12 2 views
3

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

Для быстрого примера, скажем, у меня есть шаблон структуры 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<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

Я не уверен, я понимаю, что вы имеете в виду с «статическим связыванием» в этом контексте; но что случилось с 'constexpr auto pseudostatic = A (5);'? – dyp

+0

Как вы обнаруживаете, создается ли какой-либо конкретный класс во время выполнения? – wowofbob

+1

Даже если решение dyp не применимо напрямую, 'constexpr', вероятно, является ключом к уклону от проблемы. Возможно, вам даже не понадобится шаблон 'A'. – MSalters

ответ

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 косвенность, когда вы хотите получить доступ к значению. Yuck, особенно если A предназначен для малого и быстрого легкого класса.

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

  4. Inherit value_A от basic_A. Проблема с конструкцией исчезает, но теперь basic_A не может легко получить доступ к значению value_A.Одним из решений было бы иметь чистую виртуальную функцию getValue() в basic_A, которую должны выполнить две специализации value_A. Это опять-таки имеет стоимость отправки виртуальной функции, что может быть нежелательно для небольшого легкого класса, но она позволяет инкапсулировать, поскольку basic_A является не-шаблоном и может скрыть его реализацию в файле .cpp. Другим подходом было бы использовать полиморфизм compiletime в 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() Vía 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 aka. A_value для 4b. Для 4a это alsmost то же самое, просто теряют аргументы шаблона и скобки из 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; } 
}; 
+0

'basic_A' как базовый класс также может быть CRTP-шаблоном для доступа к значению без вызова виртуальной функции. – aschepler

+0

@aschepler - это именно то, что показывает мой последний пример. Я немного реструктурирую ответ, чтобы подчеркнуть обе возможности, прежде чем показывать код. –

Смежные вопросы