英文:
Cast derived class list to base class list in C++
问题
是否可以安全使用 `reinterpret_cast` 将 `std::list<Derived *>` 转换为 `std::list<Base *>`?
```c++
class Base {
...
}
class Derived : public Base{
...
}
void func(const std::list<Base *> &list);
int main() {
std::list<Derived *> list1;
// ...
func(*reinterpret_cast<std::list<Base *> *>(&list1)); // 安全吗?
}
如果这是一个安全的转换,我们就不需要复制列表,这可能会提高性能。
<details>
<summary>英文:</summary>
Is it safe to use `reinterpret_cast` to convert `std::list<Derived *>` to `std::list<Base *>`?
```c++
class Base {
...
}
class Derived : public Base{
...
}
void func(const std::list<Base *> &list);
int main() {
std::list<Derived *> list1;
// ...
func(*reinterpret_cast<std::list<Base *> *>(&list1)); // Safe ?
}
If it's a safe cast, we don't need to copy the list, which may improve performance.
答案1
得分: 6
此代码存在未定义行为,无论容器模板如何:std::list<Base*>
和 std::list<Derived*>
是不相关的类型。甚至 std::vector<int*>
和 std::vector<const int*>
也是不相关的。
其中一个原因是,允许这些转换将允许 reinterpret_cast<std::list<Base*>&>(derived_list).push_front(base_ptr)
添加一个错误类型的指针。出于同样的原因,char**
不能隐式转换为 const char**
;不幸的是,允许的 const char* const*
转换对于容器类型没有类似的情况。C++ 类型系统并不那么强大。
显而易见的实现可能会对这种类型的转换按照您的期望进行处理,因为它们的布局都相同,但对于这样的代码,非显而易见的实现可能会以任意方式“不正常”运行。真正的答案是使 func
接受一个范围而不是特定的容器类型,这样它可以与 std::list<Derived*>
、std::vector<Base*>
以及可能还有 std::generator<std::unique_ptr<Base>>
一起工作。
英文:
This code has undefined behavior regardless of the container template: std::list<Base*>
and std::list<Derived*>
are unrelated types. Even std::vector<int*>
and std::vector<const int*>
are unrelated.
One reason for this is that allowing these conversions would allow reinterpret_cast<std::list<Base*>&>(derived_list).push_front(base_ptr)
to add a pointer of the wrong type. For the same reason, char**
cannot be implicitly converted to const char**
; unfortunately the const char* const*
conversion that is allowed has no analog for container types. The C++ type system just isn’t that powerful.
It’s true that obvious implementations will do what you expect for casts like this because the layouts are all the same, but non-obvious implementations may “misbehave” arbitrarily for code like this. The real answer is to make func
accept a range rather than a specific container type, which makes it work with std::list<Derived*>
, std::vector<Base*>
, and probably std::generator<std::unique_ptr<Base>>
as well.
答案2
得分: 5
这是错误的。即使你能重新解释将 Derived*
转换为 Base*
,你也不能以有意义、可移植的方式重新解释将 std::list<Derived*>
转换为 std::list<Base*>
。它们是两种不相关的类型。除了它们是相同模板的实例化之外,没有其他关系。
reinterpret_cast
经常被误解为任意转换,但实际上不是这样。它实际上有一套相当有限的有效用例。有关完整列表,请参见此处:https://en.cppreference.com/w/cpp/language/reinterpret_cast。将 std::list<Derived*>
转换为 std::list<Base*>
不在其中。
可能你根本不需要复制列表。如果类型是多态的,那么无需复制也无需转换,你可以使用 std::list<Base*>
和元素的虚拟方法。然而,如果你确实需要将 Derived*
转换为 Base*
,那么你可以通过 dynamic_cast
转换元素,而无需转换整个列表。也无需复制。
英文:
This is wrong. Even if you could reinterpret cast a Derived*
to a Base*
, you cannot reinterpret cast a std::list<Derived*>
to a std::list<Base*>
in a meaningful, portable way. They are two unrelated types. Being instantiations of the same template does not impose any relation between them, other than being instantiations of the same template.
reinterpret_cast
is often misunderstood as an any-to-any cast, but thats not what it is. Actually it has a rather limited set of valid use cases. For a complete list see here: https://en.cppreference.com/w/cpp/language/reinterpret_cast. Casting a std::list<Derived*>
to a std::list<Base*>
is not among them.
Probably you do not have to copy the list in the first place. If the types are polymorphic then no copy nor cast is needed, you can use the std::list<Base*>
and the elements virtual methods. If however you do need to cast a Derived*
to a Base*
then you can cast the elements via dynamic_cast
without casting the whole list. Also no copy needed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论