你如何 static_cast 一个 std::unordered_set?

How do you static_cast an std::unordered_set?

我正在尝试 static_cast unordered_set 并且我想知道如果没有未定义的行为这是否可行。

这是我想要完成的:

#include <unordered_set>

struct  Base{};
struct Derived : public Base{ Derived() = default; };

int main(void){
    std::unordered_set<Base *> set;
    set.insert(new Derived{});
    auto set_ptr{static_cast<std::unordered_set<Derived *>*>(&set)};
}

我正在尝试将static_cast一组Base *变成一组Derived *

然而这不会编译错误:

main.cpp: In function ‘int main()’:
main.cpp:21:66: error: invalid static_cast from type ‘std::unordered_set*’ to type ‘std::unordered_set*’
     auto set_ptr{static_cast<std::unordered_set<Derived *>*>(&set)};

我想知道是否有一种方法可以在不进入未定义行为领域的情况下执行此操作。

首先,您应该使用 dynamic_cast 而不是 static_cast,其中:

Safely converts pointers and references to classes up, down, and sideways along the inheritance hierarchy.

此外,您不能投射整个 std::unordered_set,但您需要 dynamic_cast std::unordered_set 的每个元素,例如:

std::unordered_set<Derived *> second;
for (auto& it : first) {
    auto derived_ptr = dynamic_cast<Derived *>(&*it);
    if (nullptr != derived_ptr) {
        second.insert(derived_ptr);
    }
}

请注意,您需要检查 derived_ptr 是否为 nullptr,因为如果 dynamic_cast 失败 ,它 returns nullptr.

在大多数情况下,您无法对模板参数执行转换,例如

MyTemplateClass<T> foo;
MyTemplateClass<U>& bar = std::static_cast<MyTemplateClass<U>&>(foo);

因为类型MyTemplateClass<U>MyTemplateClass<T>在继承结构上是完全不相关的。两者之一甚至可以专门化为完全不同的东西!

在你的情况下 MyTemplateClassstd::unordered_set

在指针容器的情况下,例如 std::set<T*> 我们对包含的内容有更多的了解:指针,并且可以做的事情很少:

  • 丑。非标准。危险的。按照标准,这是未定义的行为。

    就做一个reinterpret_cast<std::unorederd_set<Derived*>&>。它在大多数情况下都有效。但现在不破坏东西完全是你的责任。例如,当使用 std::unorederd_set<Derived*> 引用时,您必须确保集合中没有 Base* 元素。当你将它传递给函数或存储为某个对象的字段时,它会很容易忘记。

  • 干净但千篇一律。

    写一个适配器。您自己的 std::unordered_set<Derived*> 实现,它在下面保存对 std::unordered_set<Base*> 的引用,并在您每次访问元素时执行所有必要的转换。

    例如,您很可能需要在其上编写自己的迭代器。访问器 operator* 将执行 static_castdynamic_cast 来访问其元素。或者更好的是,让迭代器仅在实际上是 Derived* 的元素上停止并跳过所有其他 Base*.