我开始使用C ++模板只是因为我想了解与其他语言(Java)的具体差异,并且我达到了他们开始分歧的程度,但我没有得到我应该如何解决具体问题问题(或解决它)。
假设我有一个通用值类,例如
template <class T>
class Value
{
protected:
T value;
public:
Value(Type type, T value) : type(type), value(value) {}
void set(T value) { this->value = value; }
T get() const { return this->value; }
T clone() { return new Value<T>(type, value); }
virtual string svalue() const = 0;
const Type type;
};
和特定的子类型:
class Int : public Value<int>
{
public:
Int(int value) : Value<int>(INT, value) { };
virtual string svalue() const { ... }
friend ostream& operator<<(ostream& os, const Int& v);
};
(我知道也可以通过使用template <>
来指定特定于类型的代码,但是因为我仍然需要使用它来理解它我现在只是由自己的Int类定义,这不过是一个最后是typedef Value<int>
)
是否有可能拥有一个能够存储Value
个实例的任意指针的集合?无需指定泛型类的特定具体类型。
据我所知,模板只是一个编译时问题,编译器会分析所有使用该模板的具体类型,并为每个类型编译相同方法的不同版本,因此我正在尝试做什么似乎不可能(在Java中我可以使用通配符来代替List<Value<?>>
)。我错了吗?
是否有一个共同的设计来解决这个问题,或者我被迫放弃模板来实现它?
答案 0 :(得分:3)
#include <iostream>
#include <memory>
class Base
{
public: virtual void Print() = 0;
};
template<typename T>
class Derived : public Base
{
T V;
public:
void Print() { std::cout << V; }
Derived(T v) : V(v) { }
};
int main()
{
std::unique_ptr<Base> Ptr (new Derived<int>(5));
Ptr->Print();
return 0;
}
我认为这是不言自明的。
答案 1 :(得分:2)
是否可以拥有一个能够存储的集合 任意指向值实例的指针?
不,不是你想要的方式。这是不可能的:
template <class T>
class Value
{
// ...
};
vector<Value> my_values_;
这是不可能的,因为Value
不是一种类型 - 它实际上只是一个蓝图,而且如果你愿意的话。除了Philisophical ramblings,你不能存储想法,你只能存储东西。 Value
不是一件事。
如果这是您想要的,那么模板可能是该作业的错误工具。您可能真正关注的小麦是抽象基类,其中基类(比如class Value
)定义接口,子类(比如class Int : public Value
)定义具体类型。这样,您可以使用指针创建通用Value
s的容器:
vector<Value*> my_values_;
或者,更好地使用智能指针:
vector<unique_ptr<Value>> my_values_;
答案 2 :(得分:1)
Java技术可以通过公共基类(参见Bartek的其他答案)和类型擦除等技术在C ++中完成。
C ++版本中的值实际上是值,不能用Java完成。如果我没记错的话,它可以用一些编译成Java字节代码的语言来完成。
在Java中,你可以获得的唯一对象实际上更像是垃圾收集指向C ++中对象的指针。直接存储或引用的实际对象的实际实例是verbotin,因为这会妨碍Java样式的垃圾收集。
因此Java中的Value<?>
容器类似于指向C ++中垃圾收集的所有Value
类型的公共基类的指针容器。然后,对每个实例的访问涉及Java中的dynamic_cast
或static_cast
等效项。
对于更多Java esque行为,给Value一个带有虚拟平凡析构函数的公共基础,在所有实例上具有相同签名的纯虚拟公共方法,用不同签名实现事物的模板版本,以及生成{{{ 1}}到值实例。
将shared_ptr
的容器用于Value base,并在需要时使用动态共享ptr强制转换来获取特定接口。
现在所有这些意味着你的代码比没有所有结构的代码慢10到100倍,但它可能仍然比同等的Java版本更快。如果您不需要它,您可以选择不使用它。
答案 3 :(得分:1)
我总是喜欢混淆问题并抛出一个很好的语法扭曲,尽管它仍然只是做同样的事情(使用一个共同的基类)。唯一奇怪的是Value<T>
的基类是拼写Value<>
并且可以在容器中使用(当然,不是直接使用,因为你需要使用一个点来避免切片):
#include <memory>
#include <vector>
template <typename T = void>
class Value;
template <>
class Value<void>
{
public:
virtual ~Value() {}
};
template <typename T>
class Value
: public Value<>
{
T value_;
public:
Value(T value): value_(value) {}
// whatever
};
template <typename T>
std::unique_ptr<Value<T>> make_value(T value) {
return std::unique_ptr<Value<T>>(new Value<T>(value));
}
int main()
{
std::vector<std::unique_ptr<Value<>>> values;
values.push_back(make_value(0));
values.push_back(make_value(0.0));
values.push_back(make_value(false));
}
答案 4 :(得分:0)
是否可以拥有一个能够存储的集合 任意指向值实例的指针?
不,它不会起作用。但是,至少有可能:
如果您事先知道要在列表中使用的每种类型,可以使用boost::variant
您可以列出指向对象的指针(实际上是void*
,或者您可以删除模板并将Value
作为基类)并以某种方式(例如dynamic_cast
)将它们转换为一些特定的对象。