将模板 class 的实例添加到向量 (C++)
Adding instances of a template class to a vector (C++)
在下面的代码中,我在尝试将 fooBaz
推入 v
时遇到编译器错误。这让我感到惊讶,因为 Baz
是 Bar
.
的派生 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>
那么你也 cannot 将 Baz
推到那个向量。为什么不看这里: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);
这不起作用,因为 fooBar
和 fooBaz
是不相关的类型。
现在,在 c++23 反射之前,很难在 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));
这编译。
在 c++23 编译时反射应该让你自动检测 Baz
的基 classes 并且 Foo<Baz>
自动继承自 Foo<Bases>...
如果你要。
现在,继承只是 C++ 中处理多态性的一种方式,但我认为今天已经足够了。
在下面的代码中,我在尝试将 fooBaz
推入 v
时遇到编译器错误。这让我感到惊讶,因为 Baz
是 Bar
.
为什么这是不允许的,如果我想把几个 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>
那么你也 cannot 将 Baz
推到那个向量。为什么不看这里: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);
这不起作用,因为 fooBar
和 fooBaz
是不相关的类型。
现在,在 c++23 反射之前,很难在 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));
这编译。
在 c++23 编译时反射应该让你自动检测 Baz
的基 classes 并且 Foo<Baz>
自动继承自 Foo<Bases>...
如果你要。
现在,继承只是 C++ 中处理多态性的一种方式,但我认为今天已经足够了。