Как вы static_cast std :: unordered_set? - PullRequest
1 голос
/ 17 марта 2020

Я пытаюсь static_cast unordered_set и хочу знать, возможно ли это без неопределенного поведения.

Вот что я пытаюсь выполнить sh:

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

Мне было интересно, есть ли это способ сделать это, не входя в неопределенную область поведения.

Ответы [ 2 ]

1 голос
/ 17 марта 2020

В большинстве случаев вы не можете выполнять приведение в отношении аргумента шаблона, например

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

, поскольку типы MyTemplateClass<U> и MyTemplateClass<T> совершенно не связаны с точки зрения структуры наследования. Один из двух может даже быть специализированным, чтобы быть совершенно другим!

В вашем случае MyTemplateClass - это std::unordered_set.

В случае контейнера указателей, например std::set<T*> у нас есть немного больше знаний о том, что содержится: указатели, и есть несколько вещей, которые можно сделать:

  • Уродливо. Нестандартный. Dangerous. По стандарту это неопределенное поведение.

    Просто сделайте reinterpret_cast<std::unorederd_set<Derived*>&>. Это будет работать в большинстве случаев. Но теперь ваша ответственность не сломать вещи. Например, вы должны убедиться, что в наборе нет элемента Base*, когда используется ссылка std::unorederd_set<Derived*>. Это будет очень легко забыть, когда вы передадите его функции или сохраните в виде поля какого-либо объекта.

  • Чисто, но безупречно.

    Напишите адаптер. Ваша собственная реализация std::unordered_set<Derived*>, которая содержит ссылку на std::unordered_set<Base*> внизу и выполняет все необходимые преобразования в пути, каждый раз, когда вы получаете доступ к элементу.

    Например, вам, скорее всего, нужно будет написать свой собственные итераторы над ним. Аксессор operator* выполнит static_cast или dynamic_cast для доступа к своему элементу. Или еще лучше: пусть итератор останавливается только на элементах, которые на самом деле Derived* и пропускают все остальные Base*.

1 голос
/ 17 марта 2020

Во-первых, вместо static_cast вы должны использовать dynamic_cast, который:

Безопасно преобразует указатели и ссылки на классы вверх, вниз и вбок вдоль наследования иерархия.

Кроме того, вы не можете разыграть целые 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 не удается , он возвращает nullptr.

...