簡體   English   中英

reinterpret_cast指向基類指針向量的向量

[英]reinterpret_cast vector of pointers to vector of pointers to base class

考慮以下代碼

#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>

struct Base {
    int x;

    Base(int x) : x(x) {}
};

struct Derived : public Base {
    int y, z;

    Derived(int x) : Base(x), y(x + 1), z(x + 2) {}
};

void update(const std::vector<std::shared_ptr<const Base>>& elements) {
    for (const auto elem : elements) {
        std::cout << elem->x << "\n";
    }
}

int main(int, char**) {
    std::vector<std::shared_ptr<Derived>> elements(4);

    {
        int ctr = 0;
        std::generate(begin(elements), end(elements), [&ctr]() { return std::make_shared<Derived>(++ctr); });
    }

//    update(elements); // note: candidate function not viable: no known conversion from 'vector<shared_ptr<Derived>>' to 'const vector<shared_ptr<const Base>>' for 1st argument
    update(reinterpret_cast<std::vector<std::shared_ptr<const Base>>&>(elements));  // ok

    return 0;
}

我的問題是如果使用reinterpret_caststd::vector<std::shared_ptr<Derived>>std::vector<std::shared_ptr<const Base>>&並且是可行的並且被標准接受。

我用clang-3.8和gcc-6.1用-fsanitize=undefined編譯了代碼,看起來沒問題。 但是,我似乎無法找到關於cppreference的正確解釋。

當然,我可以輕松地創建一個適當的函數,但它比單行reinterpret_cast更長,並且需要一個臨時向量。

void update(const std::vector<std::shared_ptr<Derived>>& elements) {
    std::vector<std::shared_ptr<const Base>> casted(elements.size());
    std::copy(begin(elements), end(elements), begin(casted));
    update(casted);
}

像這樣的reinterpret_cast是未定義的行為。 Derived*Base*時,代碼將中斷需要指針調整。 Derived使用多重繼承而Base不是它的第一個基類時,很可能會發生這種情況。

struct Derived : public X, public Base { ... };

Derived* d = new Derived;

Base* b = d; // this is no longer a "no-op", since the Base sub-object
             // of Derived is not at offset 0:
             //
             // d  b
             // |  |
             // v  v
             // [Derived]
             // [X][Base]

如果您的目標是讓它以最簡潔的方式工作而不是通過臨時向量來避免轉換,那么在這種特殊情況下,您可以使用本答案中提出的container_cast實用程序。

update(container_cast(elements));

一般的模板和容器(我將shared_ptr視為一種特殊形式的容器)在C ++中是不協變的。 這意味着如果您有兩種類型BaseDerived < Base以及一個template<typename T> class X {}; X<Base>X<Derived>是兩個完全不同的東西,而不是任何形式的關系。

在您的情況下,您有一個類型為std::vector<std::shared_ptr<Derived>> ,然后創建一個std::vector<std::shared_ptr<const Base>>& ,然后用它來訪問它。 我認為這有兩個問題:

  1. 您將vector類型的對象vector轉換為引用類型。 我真的很想知道為什么會這樣。
  2. 您可以通過不相關的不同類型的引用來訪問該對象。 我認為這違反了嚴格的別名規則,因此是未定義的行為。

如果使用gcc -fstrict-aliasing編譯代碼,編譯器將假定您的程序符合規則並對其進行優化。 它會生成一個警告:

> Start prog.cc: In function 'int main(int, char**)': prog.cc:33:80:
> warning: dereferencing type-punned pointer will break strict-aliasing
> rules [-Wstrict-aliasing]
>      update(reinterpret_cast<std::vector<std::shared_ptr<const Base>>&>(elements));  // ok

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM