C ++模板 - 具有模板化类型的通用方法

时间:2012-12-21 16:03:44

标签: c++ templates generics

我开始使用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<?>>)。我错了吗?

是否有一个共同的设计来解决这个问题,或者我被迫放弃模板来实现它?

5 个答案:

答案 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_caststatic_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)

  

是否可以拥有一个能够存储的集合   任意指向值实例的指针?

不,它不会起作用。但是,至少有可能:

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

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