将模板 class 的实例添加到向量 (C++)

Adding instances of a template class to a vector (C++)

在下面的代码中,我在尝试将 fooBaz 推入 v 时遇到编译器错误。这让我感到惊讶,因为 BazBar.

的派生 class

为什么这是不允许的,如果我想把几个 Foo 实例,模板化在从同一个基础 class 派生的 classes 上,到一个向量中,我该怎么办?

#include <iostream>
#include <vector>

template<typename T>
class Foo {};

struct Bar {};
struct Baz : public Bar {};

int main() {
  
  Foo<Bar> fooBar;
  Foo<Baz> fooBaz;
  
  std::vector<Foo<Bar>> v;
  v.push_back(fooBar);
  v.push_back(fooBaz);

  return 0;
}

如评论中所述,模板就像食谱。您需要实例化一个 class 模板以获得 class,然后它只是一个模板。不同实例之间没有隐式关系(除了是同一模板的实例)。当您考虑以下示例时,这可能会变得更加清楚:

template<typename T> struct Foo {};

template <> struct Foo<int> { void bar(){} };
template <> struct Foo<double> { void moo(){} };

int main() {
    Foo<int> x;
    x.bar();
    Foo<double> y;
    y.moo();
}

Foo<int>Foo<double>是两个不相关的类型,方法完全不同。如果它们不是模板的实例化,而是具有不同成员的“普通”类型,您不会对无法将 FooA 推入 std::vector<FooB>.

感到惊讶

A std::vector<Foo<Bar>> 只能容纳 Foo<Bar> 类型的元素。 vector不知道这个类型是实例化模板的结果Foo。即使是,您也不能将 Foo<Bar> 分配给 Foo<Baz>,除非您提供转换。


其实你的误解还有很多需要揭穿的。假设你有一个 std::vector<Bar> 那么你也 cannotBaz 推到那个向量。为什么不看这里:What is object slicing?. And here for what to do instead: .

Java泛型与 C++ 模板不同。

class 类型的 C++ 值与 Java 类型的 class 引用变量不同。

你 运行 遇到了这两个问题。

C++ 模板为每组模板参数生成一个新的、不相关的类型。你可以创建一个公共基础,但你必须自己做。

Java 泛型在幕后实际上创建了一个 class。然后它在输入和输出处写入转换操作。

所以 Java 泛型,Foo<Base>Foo<Derived> 是相关的,因为 Java 泛型实际上创建了一个 Foo<Object> 然后将其包装在强制转换,Foo<Base>Foo<Derived> 中的强制转换是兼容的。 (好吧,并不总是 Object,你用 Java 用来确定它编写泛型的实际类型的信息来标记泛型参数,但这给了你想法)。

在 C++ 中,没有关系。 (好吧,模板模式匹配给你一个编译时关系,但根本没有运行时关系)

第二个问题是您将 class 类型的值视为引用。在 C++ 中,Foo 是一个实际的 foo。它代表一块内存,是 class 的一个实例。在 Java 中,Foo 是指向堆上某处遵守 Foo 协议的对象的智能指针(是派生的 class)。

您不能轻易地在 Java 中创建类型 Foo 的值,并且您不能轻松地在 C++ 中创建指向 Foo 的标记和清除智能指针。

Foo<Bar> fooBar;
Foo<Baz> fooBaz;

这是两种不相关的类型。它们存储在堆栈中(自动存储)。

std::vector<Foo<Bar>> v;

这存储了一个内存缓冲区,其中包含打包在一起的 Foo<Bar> 个对象。

v.push_back(fooBar);

这会将一个 fooBar 实例从自动存储复制到 vector

v.push_back(fooBaz);

这不起作用,因为 fooBarfooBaz 是不相关的类型。

现在,在 反射之前,很难在 C++ 中模仿 Java 所做的事情。您必须手动执行一些步骤。

首先,指导 Foo 理解继承 当手动告知时:

struct empty_t {};
template<class T, class Base=empty_t>
class Foo:Foo<Base> {};
template<>
class Foo<empty_t, empty_t> {
  virtual ~Foo() {}
};

struct Bar {};
struct Baz : public Bar {};


auto fooBar = std::make_unique<Foo<Bar>>();
auto fooBaz = std::make_unique<Foo<Baz, Bar>>();

std::vector<std::unique_ptr<Foo<Bar>>> v;
v.push_back(std::move(fooBar));
v.push_back(std::move(fooBaz));

这编译。

编译时反射应该让你自动检测 Baz 的基 classes 并且 Foo<Baz> 自动继承自 Foo<Bases>... 如果你要。

现在,继承只是 C++ 中处理多态性的一种方式,但我认为今天已经足够了。