Сокращение аналогичных членов данных

Следующий код иллюстрирует проблему:

struct Data {
int numFriends, numAcquaintances, numDates;
// etc...
};

enum Country {USA, Canada, China}; // etc...
enum Personality {Serious, Lazy, Funny}; // etc...
enum Height {SuperShort, Short, Medium, Tall, SuperTall};

class Religion {};

class Person {
std::map<Country, Data> countryStats;
std::map<Personality, Data> personalityStats;
std::map<Religion*, Data> religionStats;
std::map<Height, Data> heightStats;  // And suppose there are 20 such similar data members

// The problem:
void insertNumFriends (Country country, int num) {countryStats[country].numFriends = num;}
void insertNumFriends (Personality personality, int num) {personalityStats[personality].numFriends = num;}
void insertNumFriends (Religion* religion, int num) {religionStats[religion].numFriends = num;}
// and tons of other necessary methods (how to capture all of these using templates?);

Вот (неполное) решение, которое я придумала, и даже если закомментированные строки должны были скомпилироваться, это все еще не очень хороший метод из-за всех операторов if (на самом деле, это так же долго, как оригинальный метод):

#include <iostream>
#include <map>
#include <typeinfo>
#include <typeindex>

struct Data {
int numFriends, numAcquaintances, numDates;
// etc...
};

enum Country {USA, Canada, China, France}; // etc...
enum Personality {Serious, Lazy, Funny}; // etc...
enum Height {SuperShort, Short, Medium, Tall, SuperTall};

class Religion {};

template <typename T>  // new struct here
struct Stats {
std::map<T, Data> map;
};

class Person {
Stats<Country> countryStats;
Stats<Personality> personalityStats;
Stats<Religion*> religionStats;
Stats<Height> heightStats;  // And suppose there are 20 such similar data members
//  template <typename T> static std::map<std::type_index, Stats<T>> statsMap;  // illegal
public:
template <typename T>
void insertNumFriends (const T& t, int num) {
if (typeid(T) == typeid(Country)) {countryStats.map[t].numFriends = num;}
// lines below will not compile, and it's a terrible method anyway:
//      else if (typeid(T) == typeid(Personality)) personalityStats.map[t].numFriends = num;
//      else if (typeid(T) == typeid(Religion)) religionStats.map[t].numFriends = num;
//      else if (typeid(T) == typeid(Height)) heightStats.map[t].numFriends = num;
}
void showAllStats() const {  // for testing only
for (int COUNTRY = USA; COUNTRY <= France; COUNTRY++) {
auto it = countryStats.map.find(static_cast<Country>(COUNTRY));
if (it != countryStats.map.end())
std::cout << "Country " << COUNTRY << ": " << it->second.numFriends << " friends" << std::endl;
}
// etc...
}
};int main() {
Person bob;
bob.insertNumFriends (USA, 5);
bob.insertNumFriends (France, 2);
//  bob.insertNumFriends (Funny, 3); // won't compile because insertNumFriends<T> is not complete
bob.showAllStats();  // USA: 5 friends, France: 2 friends
}

Что лучше (рабочее решение)? Шаблон ациклического посетителя или что-то в этом роде? Примечание: Person имеет элементы данных, такие как name, age, countryOfOrigin, height и т. Д. … и фактически представляет человека, а не является просто интерфейсом контейнеров (элементы данных контейнера выше являются «записью» этого человека).

1

Решение

Одно из возможных решений: эмулировать шаблоны переменных C ++ 1y

Определите функцию, содержащую данные, карту в этом случае:

template<typename T>
std::map<T,Data>& map()
{
static std::map<T,Data> _map;

return _map;
}

Теперь сделайте только одну универсальную функцию вставки:

template<typename T>
void insert_data( const T& key , const Data& data )
{
map<T>()[key] = data;
}
2

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

Да, решение Ману такое короткое и элегантное! Просто прекрасно! Вот мой код, использующий его метод, и, наконец, я обнаружил необходимость указатель на данные членов:

#include <iostream>
#include <map>

struct Data {
int numFriends, numAcquaintances, numDates;
};

enum Country {USA, Canada, France};
class Religion {} *Islam = new Religion;

class Person {
private:
template<typename T>
std::map<T, Data>& dataMap() {static std::map<T, Data> map;  return map;}           public:
template<typename T>
void insert (const T& key, int Data::*dataPtr, int num) {
dataMap<T>()[key].*dataPtr += num;  // pointer to data members finally useful!
}
void print() {  // for testing the results only
std::cout << "Number of American friends: " << dataMap<Country>()[USA].numFriends << std::endl;
std::cout << "Number of Islamic acquantances: " << dataMap<Religion*>()[Islam].numAcquaintances << std::endl;
}
};

int main() {
Person bob;
bob.insert (USA, &Data::numFriends, 10);
bob.insert (Islam, &Data::numAcquaintances, 20);
bob.print();  // USA friends = 10, Islamic acquaintances = 20
}
1

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