2

我开始使用 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);
};

(我知道也可以通过 using 指定特定于类型的代码,template <>但由于我仍然需要使用它来理解它,所以我现在只是由自己的 Int 类定义,这Value<int>最终只不过是 typedef)

比方说,是否有可能拥有一个能够存储指向Value实例的任意指针的集合?无需指定泛型类的具体具体类型。

据我了解,模板只是一个编译时问题,编译器分析使用模板的所有具体类型,并为它们中的每一个编译相同方法的不同版本,因此我想要做的不是似乎是可能的(在 Java 中,我可以使用通配符来表示类似的东西List<Value<?>>)。我错了吗?

是否有解决此问题的通用设计,或者我被迫放弃模板来实现它?

4

5 回答 5

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

我认为这是不言自明的。

于 2012-12-21T16:07:28.787 回答
2

比方说,是否有可能拥有一个能够存储指向 Value 实例的任意指针的集合?

不,不是你想要的方式。这是不可能的:

template <class T>
class Value
{
// ...
};

vector<Value> my_values_;

这是不可能的,因为Value 它不是一种类型——它实际上只是一个蓝图和想法,如果你愿意的话。抛开哲学上的胡言乱语,你不能储存想法,你只能储存东西。AValue不是东西。

如果这是您想要的,那么模板可能是该工作的错误工具。你可能真正追求的是抽象基类,其中基类(比如class Value)定义接口,子类(比如class Int : public Value)定义具体类型。这样,您可以Value使用指针创建泛型容器:

vector<Value*> my_values_;

或者,更好的是使用智能指针:

vector<unique_ptr<Value>> my_values_;
于 2012-12-21T16:11:41.777 回答
1

Java 技术可以在 C++ 中通过混合通用基类(参见 Bartek 的其他答案)和类型擦除等技术来完成。

C++ 版本,其中值实际上是值,不能在 Java 中完成。如果我没记错的话,它可以用一些编译成 Java 字节码的语言来完成。

在 Java 中,您可以获得的唯一对象实际上更像是垃圾收集的指向 C++ 中对象的指针。直接存储或引用的实际对象的实际实例是verbotin,因为这会妨碍Java 风格的垃圾收集。

因此Value<?>,Java 中的容器类似于指向ValueC++ 中垃圾收集的所有类型的公共基类的指针容器。然后,对每个实例的访问涉及 Java 中的一个dynamic_caststatic_cast等效项。

对于更类似于 Java 的行为,给 Value 一个带有虚拟平凡析构函数的公共基础,在所有实例上具有相同签名的纯虚拟公共方法,实现具有不同签名的事物的模板版本,以及产生shared_ptrs 到 Value 实例的工厂函数。

如果需要,请使用 Value base 的容器shared_ptr并使用动态共享 ptr 转换来获取特定接口。

现在所有这一切意味着您的代码比没有所有这些结构的代码慢 10 到 100 倍,但它可能仍然比等效的 Java 版本快。如果您不需要它,您可以选择不使用它。

于 2012-12-21T16:30:31.910 回答
1

我总是喜欢混淆事物并加入一个很好的语法扭曲,尽管它仍然做同样的事情(使用公共基类)。唯一奇怪的是基类 ofValue<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));
}
于 2012-12-21T19:20:48.557 回答
0

比方说,是否有可能拥有一个能够存储指向 Value 实例的任意指针的集合?

不,这行不通。但是,至少有以下可能性:

  1. 如果您事先知道要在列表中使用的每种类型,则可以使用boost::variant

  2. 您可以制作指向对象的指针列表(实际上void*,或者您可以删除模板并制作Value为基类)并以某种方式(例如dynamic_cast)将它们强制转换为某些特定对象。

于 2012-12-21T16:13:54.767 回答