Класс С++ с переменной-членом шаблона
Я пытаюсь решить проблему программирования, состоящую из объекта (назовем его Diagram), который содержит несколько параметров. Каждый параметр (класс Parameter) может быть одним из нескольких типов: int, double, complex, string - для обозначения нескольких.
Итак, мой первый инстинкт заключался в том, чтобы определить мой класс Diagram как имеющий вектор параметров шаблона, который будет выглядеть следующим образом.
class Diagram
{
private:
std::vector<Parameter<T> > v;
};
Это не компилируется, и я понимаю, почему. Итак, основываясь на рекомендациях на этой странице Как объявить члены данных, которые являются объектами любого типа в классе, я изменил свой код так:
class ParameterBase
{
public:
virtual void setValue() = 0;
virtual ~ParameterBase() { }
};
template <typename T>
class Parameter : public ParameterBase
{
public:
void setValue() // I want this to be
// void setValue(const T & val)
{
// I want this to be
// value = val;
}
private:
T value;
};
class Diagram
{
public:
std::vector<ParameterBase *> v;
int type;
};
Мне трудно понять, как вызвать функцию setValue с соответствующим шаблоном. В базовом классе ParameterBase нельзя использовать шаблонный параметр. Любая помощь приветствуется.
P.S. У меня нет гибкости в использовании boost:: any.
Ответы
Ответ 1
Вы очень близко. Я добавил несколько бит, потому что они удобны
class ParameterBase
{
public:
virtual ~ParameterBase() {}
template<class T> const T& get() const; //to be implimented after Parameter
template<class T, class U> void setValue(const U& rhs); //to be implimented after Parameter
};
template <typename T>
class Parameter : public ParameterBase
{
public:
Parameter(const T& rhs) :value(rhs) {}
const T& get() const {return value;}
void setValue(const T& rhs) {value=rhs;}
private:
T value;
};
//Here the trick: dynamic_cast rather than virtual
template<class T> const T& ParameterBase::get() const
{ return dynamic_cast<const Parameter<T>&>(*this).get(); }
template<class T, class U> void ParameterBase::setValue(const U& rhs)
{ return dynamic_cast<Parameter<T>&>(*this).setValue(rhs); }
class Diagram
{
public:
std::vector<ParameterBase*> v;
int type;
};
Затем диаграмма может делать такие вещи:
Parameter<std::string> p1("Hello");
v.push_back(&p1);
std::cout << v[0]->get<std::string>(); //read the string
v[0]->set<std::string>("BANANA"); //set the string to something else
v[0]->get<int>(); //throws a std::bad_cast exception
Похоже, ваше намерение состоит в том, чтобы хранить указатели на ресурсы в векторе. Если это так, будьте осторожны, чтобы сделать Diagram
правильным деструктором и сделать его невозможным для копирования и не копировать.
Ответ 2
В следующей реализации используются несколько возможностей С++ 11, но вы будете
в состоянии разобрать их.
#include <vector>
#include <memory>
class Parameter
{
private:
class ParameterBase {
public:
virtual ~ParameterBase() {}
virtual ParameterBase* copy() = 0;
virtual void foo() = 0;
};
template <typename T>
class ParameterModel : public ParameterBase {
public:
// take by value so we simply move twice, if movable
ParameterModel(const T& t) : t(t) {}
ParameterModel(T&& t) : t(t) {}
void foo() { t.foo(); }
ParameterModel* copy() { return new ParameterModel(*this); }
private:
T t;
};
public:
template <typename T>
Parameter(T&& t)
: pp(new ParameterModel< typename std::remove_reference<T>::type >(std::forward<T>(t))) {}
// Movable and Copyable only
Parameter(Parameter&&) = default;
Parameter& operator=(Parameter&&) = default;
Parameter(const Parameter& other) : pp(other.pp->copy()) {};
Parameter operator=(const Parameter& other) {
pp.reset(other.pp->copy());
return *this;
};
// members
void foo() { pp->foo(); }
private:
std::unique_ptr<ParameterBase> pp;
};
class Diagram
{
public:
std::vector<Parameter> v;
int type;
};
struct X {
void foo() {}
};
struct Y {
void foo() {}
};
int main()
{
Diagram d;
d.v.emplace_back(X()); // int
// parameters are copyable and can be reassigned even with different
// impls
Parameter p = d.v.back();
Parameter other((Y()));
other = p;
return 0;
}
Что делает этот код? Это скрывает тот факт, что мы используем наследование
реализовать параметры наших пользователей. Все, что им нужно знать, это
что нам нужна функция-член, называемая foo
. Эти требования
выраженное в нашем ParameterBase
. Вам нужно идентифицировать эти
и добавьте к ParameterBase
. Это в основном более
ограничительный boost::any
.
Это также очень близко к тому, что описано в разделе беседа с семантикой Sean Parent.