C ++ Виртуальный шаблонный метод - PullRequest
31 голосов
/ 01 ноября 2011

У меня есть абстрактный класс (я знаю, что он не скомпилируется таким образом, но он предназначен для понимания того, что я хочу сделать):

class AbstractComputation {
    public:
        template <class T> virtual void setData(std::string id, T data);
        template <class T> virtual T getData(std::string id);
};

class Computation : public AbstractComputation {
    public:
        template <class T> void setData(std::string id, T data);
        template <class T> T getData(std::string id, T data);
};

Поэтому, когда я звоню setData<double>("foodouble", data), я хочу, чтобы двойное число, идентифицированное foodouble (внутренний механизм, который здесь не является главной задачей), было установлено на двойные данные.

Так как это сделать?

Я думаю, что может быть смысл вводить что-то вроде virtual void setData<double>(std::string id, double data), но я не знаю, как это сделать.

Ответы [ 6 ]

47 голосов
/ 01 ноября 2011

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

Есть разные вещи, которые можно сделать здесь, чтобы обойти ограничение, в основном либо убрать статический или динамический полиморфизм.Удаление динамического полиморфизма из уравнения может быть выполнено путем предоставления типа, который не является производным от, для хранения отображений <key,value>, а затем предложения шаблона, который разрешает это только на базовом уровне:

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      m_store.setData( id, value );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      return m_store.getData<T>( id );
   }
protected:
   ValueStore m_store;
};

Теперь производные классы могут получить доступ к ValueStore из базы, и нет необходимости в полиморфизме.(Это также может быть сделано путем реализации функциональности непосредственно в AbstractComputation, но, вероятно, имеет смысл разделить проблемы)

Другой вариант - сохранить полиморфизм во время выполнения, но удалить статический полиморфизм.Это можно сделать, выполнив стирание типа в базовом классе, а затем отправив соответствующую (не шаблонную) функцию, которая принимает тип-стертые аргументы.Простейшая версия этого просто использует boost::any:

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      setDataImpl( id, boost::any( value ) );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      boost::any res = getDataImpl( id );
      return boost::any_cast<T>( res );
   }
protected:
   virtual void setDataImpl( std::string const & id, boost::any const & value ) = 0;
   virtual boost::any getDataImpl( std::string const & id ) const = 0;
};

Как стирание типов реализовано под капотом, интересно, но за рамками здесь важная часть состоит в том, что boost::any являетсяконкретный (не шаблонный) тип, который может внутренне хранить любой тип, используя стирание типов в аргументах, и в то же время позволяет безопасно извлекать данные из типов.

14 голосов
/ 16 декабря 2014

В некоторых случаях может быть достаточно переместить шаблон с уровня метода на уровень класса, например:

#include <iostream>

template<typename T>
class AbstractComputation {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "base" << std::endl;
    }
};

template<typename T>
class Computation : public AbstractComputation<T> {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "derived" << std::endl;
    }
};

int main()
{
    AbstractComputation<int> *x = new Computation<int>();

    x->setData("1", -1);

    delete x;
    return 0;
}
2 голосов
/ 01 ноября 2011

Во-первых, у вас не может быть virtual шаблонных функций.Поскольку шаблоны разрешаются во время компиляции, virtual не будет работать, так как компилятор не будет знать, какой шаблон выбрать.См. здесь , для получения дополнительной информации об этом.

2 голосов
/ 01 ноября 2011

Возможно, вы можете использовать boost::any в вашем случае.

virtual void setData(std::string id, boost::any data);

Это оболочка, которая может заключать в капсулу практически все.

Подробнеепо аналогичной теме в этом ответе .

1 голос
/ 01 ноября 2011

Если вы заранее знаете список возможных типов, препроцессор может помочь:

#define MY_CLASSES MYTYPE(int) MYTYPE(float) MYTYPE(double)

class AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data)=0;\
                       virtual void getData(std::string id, T& dst_data)=0;
      MY_CLASSES
#     undef MYTYPE
};

class Computation : public AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data){std::cout<<"writing: "<<data<<std::endl;}\
                       virtual void getData(std::string id, T& dst_data){dst_data=0;/*put your actual implementation here*/}
      MY_CLASSES
#     undef MYTYPE
};

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

1 голос
/ 01 ноября 2011

Используйте boost::any, чтобы принять данные, а затем, когда вы действительно установили, выберите из них правильный тип.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...