2015-09-17 1 views
2

У меня есть класс, который выглядит примерно так:Компиляция переменных члена класса с шаблонами на C++?

class Compound 
{ 
    void* pValue0; 
    void* pValue1; 
    void* pValue2; 
    void* pValue3; 
    void* pValue4; 
    void* pValue5; 
    void* pValue6; 
    void* pValue7; 
    void* pValue8; 
    void* pValue9; 
    void* pValueA; 
    void* pValueB; 
    void* pValueC; 
}; 

Когда я создаю новый класс соединение, я выделить дополнительную память [SizeOf (соединение) + extraSpace]. Каждый из pValue ссылается на адрес в дополнительной памяти.

Теперь я хотел бы уменьшить количество pValue в зависимости от того, какие из них мне нужны. Шаблоны кажутся подходящими.

Так что если бы я хотел класса Compound < 0A>, мне бы хотелось только pValue0 и pValueA, а затем укомплектовать компилятор всеми остальными pValues. По существу, я бы хотел, чтобы это стало следующим:

template <uint Mask = 0A> 
class Compound<Mask> 
{ 
    void* pValue0; 
    void* pValueA; 
} 

Возможно ли это? Я был близок с enable_if, но когда я попытался ограничить его конкретной маской, компилятор бросил ошибки о том, что не смог найти тип, когда case enable_if был ложным.

Спасибо всем!

+1

Почему не класс со списком указателей? –

+0

Как это отличается от хранения всех ваших значений в 'std :: vector'? Вы можете получить 'pValue0' в любое время, когда вам нужно' & v [0] '. –

+0

Вы можете достичь того, чего хотите, используя [пустую оптимизацию элемента] (http://www.cantrip.org/emptyopt.html) в сочетании с 'std :: условным' (чтобы переключиться на пустой тип, когда условие является ложным). Это было бы уродливо. – VoidStar

ответ

4

Это может сделать:

template<char...> 
struct flags_tag {constexpr flags_tag(){}; }; 

template<char...Cs> 
struct make_flags{ using type=flags_tag<Cs...>; }; 
template<char...Cs> 
struct make_flags<'0','x',Cs...>:make_flags<Cs...>{}; 
template<char...Cs> 
struct make_flags<'0','X',Cs...>:make_flags<Cs...>{}; 
template<char...Cs> 
using make_flags_t = typename make_flags<Cs...>::type; 

template<char...Cs> 
constexpr make_flags_t<Cs...> operator""_flag(){ return {}; } 

template<char> struct pValue_t; 
template<> struct pValue_t<'0'>{ void* pValue0 = 0; }; 
template<> struct pValue_t<'1'>{ void* pValue1 = 0; }; 
// ... 
template<> struct pValue_t<'A'>{ void* pValueA = 0; }; 
template<> struct pValue_t<'B'>{ void* pValueB = 0; }; 
template<> struct pValue_t<'C'>{ void* pValueC = 0; }; 

template<class flags> 
struct Compound; 

template<char...Cs> 
struct Compound< flags_tag<Cs...> >: 
    pValue_t<Cs>... 
{}; 

Затем вы используете это нравится:

using my_type = Compound< decltype(0x0A_flag) >; 
int main() { 
    my_type test; 
    std::cout << test.pValue0 << test.pValueA << '\n'; 
} 

, который, кажется, делает то, что вы хотите.

Я также отключил бы копию/перемещение ctor вашего типа Compound и сделаю его другие конструкторы private с заводской функцией friend.

Обратите внимание, что этот код может генерировать экспоненциальное число классов (2^12 или 4k) и может вызывать бинарное раздувание (если какой-либо код класса не является встроенным).

[живой пример]

+0

Это работает! Можете ли вы объяснить, почему эта часть необходимо ?: 'шаблон constexpr make_flags_t оператор "" _ флаг() {возвращение {}; } ' Я не мог понять, зачем нужна эта деталь. Я попытался сделать 'template constexpr make_flags_t operator_test() {return {}; } ' , но это не сработает. Что необходимо для части '_flag'? – pantaryl

+0

@pantaryl Это была моя попытка как можно ближе подойти к синтаксису 'Compound <0A>'. Пользовательский литерал '_flag', который потребляет символы целого типа:' 0x0A_flag' - это то, что 'template оператор" "' делает. Затем мы берем его тип (который содержит шестнадцатеричные символы) и передаем его в 'Compound '. Вы можете сделать это с помощью 'Compound ()>' или другого аналогичного синтаксиса, например 'Compound >'. – Yakk

+0

Я изменил реализацию на [link] (http://pastebin.ca/ 3164976) Однако, он отказывается компилировать, если 'operator_test' не переименовывается как' operator_flag'. Любая идея почему? – pantaryl

2

std::conditional похоже на std::enable_if, за исключением условного всегда возвращает тип.

std::conditional<satisfies(Mask), void*, EmptyClass> был бы способом выборочно менять типы элементов и скомпилировать их.

Проблема в том, что C++ не допускает пустых элементов. Размер будет 1. Чтобы решить эту проблему, вам понадобится empty member optimization. Это позволит получить макет памяти, о котором вы просите, но, к сожалению, это затруднит чтение членов класса. Вы можете добавить функции доступа, чтобы смягчить это, если вы считаете, что это стоит того.

1

Я предполагаю, что Compound класса должен содержать указатели на объекты различных классов и не недействительные указатели, как вы сказали в своем комментарии.

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

class Class0; 
class ClassA; 

template<int> struct Compound {}; 
template<> struct Compound<0A> 
{ 
    typedef std::tuple<Class0*, ClassA*> type; 
} 
// other mapping from mask to type 

typename Compound<0A>::type aCompund(ptr0, ptrA); 
Смежные вопросы