首页 > 解决方案 > 将模板类的实例添加到向量 (C++)

问题描述

在下面的代码中,我在尝试推fooBaz送到v. 这让我感到惊讶,因为Baz它是Bar.

为什么这是不允许的,如果想将多个Foo实例(以从同一基类派生的类为模板)放入向量中,我该怎么办?

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

标签: c++templatesgenericspolymorphism

解决方案


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

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

您在这里遇到了这两个问题。

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

Java 泛型在底层实际上创建了一个类。然后它在输入和输出处写入强制转换操作。

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

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

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

在 Java 中你不能轻易地创建一个类型的值,你也不能在 C++ 中Foo轻松地创建一个指向 a 的标记和清除智能指针。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是不相关的类型。

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

首先,在手动告知时指示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));

这编译。

编译时反射应该让您自动检测基类BazFoo<Baz>自动继承(Foo<Bases>...如果需要)。

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


推荐阅读