C ++ Альтернатива шаблонному элементу данных

Если у меня есть класс DataManager:

class DataManager
{
 public:
   int getRecordInt( size_t i ) const;
   std::string getRecordString( size_t i ) const;

 private:
   std::vector<int> _vInt;
   std::vector<std::string> _vString;
}

Я могу получить доступ к записям, например,

DataManager DM;
// .. populate object ...
int iValue = DM.getRecordInt(3);

В моем реальном приложении у меня будет более сотни типов данных (помимо int и std :: string), поэтому я бы не хотел писать отдельный получатель для каждого из типов.

Теперь, если C ++ будет поддерживать шаблонные имена переменных (чего нет), я мог бы реализовать класс как:

class DataManager
{
 public:
   template<typename T>
   T getRecord( size_t i ) const
   {
      return _v<T>[i];
   }

 private:
   std::vector<int> _v<int>;
   std::vector<std::string> _v<std::string>;
}

Есть ли способ достичь цели на C ++?

(Имейте в виду, что хотя я упростил пример до минимума, моя реальная проблема намного сложнее и требует существования такого DataManager класса.)


person user7065361    schedule 26.07.2017    source источник
comment
После второй гипотетической реализации класса DataManager я мог бы просто вызвать int iValue = DM.getRecord<int>(3);. Я надеюсь, что идея, которую я преследую, ясна.   -  person user7065361    schedule 26.07.2017
comment
Я не думаю, что это возможно. он подпадает под категорию отражения, которая пока не поддерживается.   -  person David Haim    schedule 26.07.2017
comment
что вы имеете в виду под шаблонными именами переменных? В вашем последнем коде член _v std::vector<int> не зависит от того, как вызывается getRecord, и неясно, что вы имеете в виду с _v<int> в объявлении.   -  person 463035818_is_not_a_number    schedule 26.07.2017


Ответы (4)


C ++ 14 Решение:

Вы можете поместить все разные векторы в std::tuple. Затем вы можете использовать std::get, чтобы указать, какой вектор получить из кортежа. а затем получить доступ к этому вектору. Это будет выглядеть как

class DataManager
{
 public:
   template<typename T>
   T getRecord( size_t i ) const
   {
      return std::get<std::vector<T>>(_v)[i];
   }

 private:
   std::tuple<std::vector<int>, std::vector<std::string>> _v{{1,2,3},{"a","b","c"}};
};

int main()
{
    DataManager d;
    std::cout << d.getRecord<std::string>(2);
}

Какие выходы

c

Живой пример

person NathanOliver    schedule 26.07.2017
comment
Спасибо. Это то, что мне нужно, но, к сожалению, я могу использовать только C ++ 03 в проекте (забыл упомянуть об этом в вопросе), и эта версия std :: get (), похоже, была представлена ​​в C ++ 14 . Любые идеи в C ++ 03? - person user7065361; 26.07.2017
comment
@ user7065361 Если вы не можете использовать C ++ 11/14, то я думаю, что ответ Сержа Баллеста таков, как вам нужно. Или вы можете использовать boost, у которого есть boost::tuple. - person NathanOliver; 26.07.2017
comment
Боюсь, что с помощью кортежа невозможно хранить «более сотни типов данных». - person duong_dajgja; 26.07.2017

Если ваши типы появляются только один раз, вы можете использовать std::tuple и std::get:

class DataManager
{
public:
   template<typename T>
   T getRecord( size_t i ) const
   {
      return std::get<std::vector<T>>(v)[i];
   }

 private:
   std::tuple<std::vector<int>, std::vector<std::string>> v;
}
person nh_    schedule 26.07.2017
comment
Тот же ответ, который я написал Натану: спасибо, ваше решение - это то, что мне нужно, но, к сожалению, я могу использовать только C ++ 03 в проекте (забыл упомянуть об этом в вопросе), и эта версия std :: get ( ), похоже, был введен в C ++ 14. Есть идеи в C ++ 03? - person user7065361; 26.07.2017
comment
Если у вас есть C ++ 03, у вас есть доступ к tuple и type_traits, которые являются всем, что нужно для реализации get<type>, это относительно просто, на самом деле я почти уверен, что однажды видел пример реализации на cppreference.com . Даже если нет, я действительно реализовал get<type> как часть моей библиотеки backports для C ++ прямо из предложения, которое было снято для C ++ 14. Возможно, вы сможете использовать код в качестве основы. - person Luis Machuca; 30.07.2017

У вас не может быть шаблонной переменной, но вы можете иметь частный шаблонный метод, который возвращает соответствующую переменную:

class DataManager
{
 public:
   template<typename T>
   T getRecord( size_t i ) const
   {
      return _v<T>()[i];
   }

 private:
   std::vector<int> _vInt;
   std::vector<std::string> _vString;
   template<typename T> std::vector<T>& _v() {
       throw std::logic_error("non managed type");
   }
};

template <> std::vector<int>& DataManager::_v<int>() {
   return _vInt;
}
template <> std::vector<std::string>& DataManager::_v<std::string>() {
   return _vString;
}

Вам решать, является ли это приемлемым решением для вашего реального варианта использования.

person Serge Ballesta    schedule 26.07.2017
comment
Просто любопытно, почему вы генерируете исключение вместо использования static_assert. Поскольку вызов будет разрешен во время компиляции, не лучше ли ошибка? - person NathanOliver; 26.07.2017
comment
Легче ли написать отдельную специализацию шаблона для каждого типа, чем отдельный геттер? - person Chris Drew; 26.07.2017
comment
@NathanOliver: вероятно, использование Java (здесь неправильно) ... и мне лень создавать std::if, который для этого потребуется. Заполните, если хотите, отредактируйте мой ответ. - person Serge Ballesta; 26.07.2017
comment
@ChrisDrew: это может зависеть от фактического варианта использования. Это причина моего последнего предложения ... - person Serge Ballesta; 26.07.2017
comment
@NathanOliver: А в C ++ 03 нет static_assert :-) - person AndyG; 26.07.2017
comment
Спасибо, Серж, подойдет! Для моего приложения мне нужно вызвать getRecord<T>() из другой функции шаблона, поэтому простые геттеры не подходили, поскольку они не позволили бы мне передать тип. - person user7065361; 26.07.2017

Если ответ Сержа Баллесты не позволяет вашей лодке плавать (и ИМХО, это правильный способ продолжить), тогда вы можете зубы и напишите макрос (решение C ++ 03):

#define ADD_RECORD_TYPE(TYPENAME, QUALIFIED_TYPENAME) \
private: \
    std::vector<QUALIFIED_TYPENAME> _v##TYPENAME; \
public:  \
    QUALIFIED_TYPENAME get_record_##TYPENAME(size_t i) const{return _v##TYPENAME.at(i);}

class DataManager
{
    ADD_RECORD_TYPE(string, std::string)
    ADD_RECORD_TYPE(int, int)
};
#undef ADD_RECORD_TYPE

Демо

person AndyG    schedule 26.07.2017