【问题标题】:C++ Templates - Having generic methods for templated typesC++ 模板 - 具有模板类型的通用方法
【发布时间】:2012-12-21 16:03:43
【问题描述】:

我开始使用 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 &lt;&gt; 来指定特定于类型的代码,但由于我仍然需要使用它来理解它,所以我现在只是由自己的 Int 类定义,这只不过是 typedef @ 987654324@到底)

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

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

是否有一个通用的设计来解决这个问题,或者我不得不放弃模板来实现它?

【问题讨论】:

  • 是的,让Value 继承自一个通用类型。

标签: c++ templates generics


【解决方案1】:
#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;
}

我认为这是不言自明的。

【讨论】:

    【解决方案2】:

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

    不,不像你想要的那样。这是不可能的:

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

    这是不可能的,因为Value 不是一个类型——如果你愿意的话,它实际上只是一个蓝图和想法。撇开哲学散文不谈,你不能储存想法,你只能储存东西。 Value 不是一个东西。

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

    vector<Value*> my_values_;
    

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

    vector<unique_ptr<Value>> my_values_;
    

    【讨论】:

      【解决方案3】:

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

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

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

      因此,Java 中Value&lt;?&gt; 的容器类似于指向所有Value 类型的公共基类的指针容器,该基类在C++ 中被垃圾收集。然后,对每个实例的访问涉及到 Java 中的 dynamic_caststatic_cast 等效项。

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

      shared_ptr 的容器用于Value base,并在需要时使用动态共享ptr cast 来获取特定接口。

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

      【讨论】:

        【解决方案4】:

        我总是喜欢混淆事物并抛出一个很好的语法扭曲,尽管它仍然做同样的事情(使用一个公共基类)。唯一奇怪的是Value&lt;T&gt; 的基类拼写为Value&lt;&gt; 并且可以在容器中使用(当然不是直接使用,因为您需要使用点来避免切片):

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

        【讨论】:

          【解决方案5】:

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

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

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

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

          【讨论】:

          • 如果你真的需要dynamic_cast,你不应该把所有的对象都存储在一个容器中,因为这样做有什么意义呢?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-10-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多