Подтвердить что ты не робот

Виртуальный метод шаблона С++

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

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), но я не знаю, как это сделать.

4b9b3361

Ответ 1

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

Существуют разные вещи, которые можно сделать здесь, чтобы обойти ограничение, в основном либо убрать статический, либо динамический полиморфизм. Удаление динамического полиморфизма из уравнения может быть выполнено путем предоставления типа, из которого не производятся, для хранения отображений <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, но, вероятно, имеет смысл разделить проблемы)

Другой вариант - поддерживать полиморфизм во время выполнения, но удалять статический полиморфизм. Это можно сделать, выполнив стирание типа в базовом классе, а затем отправив соответствующую функцию (не templated), которая принимает аргументы с стиранием типа. Простейшая версия этого просто использует 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 представляет собой конкретный (не шаблонный) тип, который может хранить любой тип внутри, используя стирание типа на аргументы и в то же время допускают безопасное извлечение данных.

Ответ 2

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

#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;
}

Ответ 3

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

Ответ 4

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

Ответ 6

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

#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
};

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