Можно ли создать нестатическое поле шаблона в классе?
Если нет, как обойти?
Такие поля должны быть созданы во время компиляции по мере необходимости.
у меня много B
-класс, как B1
,B2
,B3
,
(В реальном случае они имеют более значимые имена.)
Я хочу создать класс D
который имеет нестатическая функция шаблона add<BX>()
что должно counter++
каждый раз, когда я это называю, для каждого человека BX
, для определенного случая D.
(В реальном случае это делает что-то более сложное.)
Вот рабочая демонстрация добиться этого.
К сожалению, в настоящее время я должен жестко кодировать каждый BX
, по одному (B1
,B2
,B3
) внутри D
: —
class B1{};class B2{};class B3{};
class Counter{
public: int counter=0;
};
template<class BX>class Tag{};
class D{
Counter countB1;
Counter countB2;
Counter countB3;
public: template<class BX> void add(){
add_(Tag<BX>());
}
private:
void add_(Tag<B1>){ countB1.counter++;}
void add_(Tag<B2>){ countB2.counter++;}
void add_(Tag<B3>){ countB3.counter++;}
public: template<class BX> int get(){
return get_(Tag<BX>());
}
private:
int get_(Tag<B1>){ return countB1.counter;}
int get_(Tag<B2>){ return countB2.counter;}
int get_(Tag<B3>){ return countB3.counter;}
};
Вот использование. Обратите внимание, что каждый экземпляр D
держать свой собственный counter
: —
int main() {
D d1;
d1.add<B2>(); d1.add<B2>(); d1.add<B3>();
std::cout<<d1.get<B1>()<<" "<<d1.get<B2>()<<" "<<d1.get<B3>()<<"\n";
//^ print 0 2 1
D d2;
d2.add<B1>();
std::cout<<d2.get<B1>()<<" "<<d2.get<B2>()<<" "<<d2.get<B3>()<<"\n";
//^ print 1 0 0 (not 1 2 1)
return 0;
}
Я мечтаю о чем-то вроде: —
class D{
Counter<BX> countBX; //???
public: template<class BX> void add(){
Counter<BX>::getNonStaticInstance(this).counter++; //???
}
public: template<class BX> int get(){
return Counter<BX>::getNonStaticInstance(this).counter; //???
}
};
Я знаю как это сделать если countBX
является статическим, но для нестатического это кажется невозможным.
Используя std::map
std::unordered_map
(предложение от Yakk; спасибо) индексов и RTTI?
#include <map>
#include <iostream>
#include <typeindex>
class B1 {};
class B2 {};
class B3 {};
class D
{
private:
std::unordered_map<std::type_index, std::size_t> bxMap;
public:
template <typename BX>
void add ()
{ ++ bxMap[std::type_index(typeid(BX))]; }
template <typename BX>
int get ()
{ return bxMap[std::type_index(typeid(BX))]; }
};
int main ()
{
D d1;
d1.add<B2>(); d1.add<B2>(); d1.add<B3>();
std::cout<<d1.get<B1>()<<" "<<d1.get<B2>()<<" "<<d1.get<B3>()<<"\n";
//^ print 0 2 1
D d2;
d2.add<B1>();
std::cout<<d2.get<B1>()<<" "<<d2.get<B2>()<<" "<<d2.get<B3>()<<"\n";
//^ print 1 0 0
return 0;
}
Вам не нужно RTTI
чтобы решить эту проблему, ни std::map
, которые очень дорогие (особенно RTTI). Шаблон Variadic и наследование могут решить эту проблему для вас:
class B1 {}; class B2 {}; class B3 {};
template<typename T>
class Counter {
public:
int counter = 0;
};
template<class... BXs>
class D : public Counter<BXs>... {
public:
template<typename B>
void add() {
Counter<B>::counter++;
}
template<typename B>
int get() {
return Counter<B>::counter;
}
};
Что очень близко к тому, что вы на самом деле хотели (кстати, вы были на правильном пути).
К сожалению, пока мы не получим отражение в стандарте, не будет простого способа перебора членов класса.
Решения до тех пор либо включают реализацию рефлексии самостоятельно (сложно и часто используют макросы, которые идут со своими собственными проблемами, такими как отладка), либо оборачивают ваши типы в другой тип (проще).
Мы можем сделать это с базовым классом, который имеет std::array
счетчиков, по одному для каждого BX
:
template<class... Bs>
struct Base
{
std::array<Counter, sizeof...(Bs)> counters;
// ... more on this later
};
Тогда наш D
класс может извлечь из него и получить нужные ему счетчики:
struct D : Base<B1, B2, B3>{ /*...*/};
Следующее, что мы сделаем, это осуществим IndexOf
функция в базовом классе, которая позволит нам преобразовать тип (один из B1
B2
B3
) в индекс.
Мы можем сделать это с помощью признаков типа и выражений сгиба:
template<class T>
static constexpr int IndexOf()
{
// find index of T in Bs...
int toReturn = 0;
int index = 0;
(..., (std::is_same_v<T, Bs> ? toReturn = index : ++index));
return toReturn;
}
А теперь наш D
класс значительно упрощен и не зависит от отправки тегов:
struct D : Base<B1, B2, B3>{
template<class BX>
void add(){
counters[IndexOf<BX>()].counter++;
}
template<class BX>
int get(){
return counters[IndexOf<BX>()].counter;;
}
};
C ++ 14 версия IndexOf
:
template<class T>
static constexpr int IndexOf()
{
// find index of T in Bs...
int toReturn = 0;
int index = 0;
using swallow = int[];
(void) swallow {0, (std::is_same<T, Bs>() ? toReturn = index : ++index, 0)...};
return toReturn;
}