簡體   English   中英

為什么gcc警告用std :: tuple和虛擬繼承調用一個非平凡的移動賦值運算符?

[英]Why does gcc warn about calling a non-trivial move assignment operator with std::tuple and virtual inheritance?

在以下示例中,gcc 7發出警告:

“B”的默認移動分配稱為虛擬基礎'A'的非平凡移動賦值運算符[-Wvirtual-move-assign]

如果我創建一個std::tuple<B>對象。 Clang 5沒有報告任何問題。 如果從Base刪除vector ,問題就會消失。 例子

#include <tuple>
#include <vector>

class Base
{
public:
    virtual ~Base();
    std::vector<int> v;
};

class A : public Base
{
};

class B : public virtual A
{
};

int main()
{
    B *b = new B;
    B another{*b}; // <<<--- this compiles
    std::tuple<B> t; // <<<--- gives warning
}

為什么會出現std::tuple (並且沒有移動賦值),如果我需要保持這樣的層次結構,修復它的正確方法是什么?

警告與tuple無關,它由B的移動分配觸發。 例如,以下代碼生成警告

B b;
B t;
t = std::move(b);

gcc文檔解釋了警告的意圖

 -Wno-virtual-move-assign 

禁止使用非平凡的C ++ 11移動賦值運算符從虛擬基礎繼承警告。 這是危險的,因為如果虛擬基地可以沿多個路徑到達,則會多次移動,這可能意味着兩個對象最終都處於移動狀態。 如果編寫移動賦值運算符以避免從移動的對象移動,則可以禁用此警告。

這是一個演示問題的示例

struct Base
{
    Base() = default;
    Base& operator=(Base&&)
    {
        std::cout << __PRETTY_FUNCTION__ << '\n';
        return *this; 
    }
};

struct A : virtual Base {};

struct B : virtual Base {};

struct C : A, B {};

int main()
{
    C c;
    c = C();
}

這會產生輸出

Base& Base::operator=(Base&&)
Base& Base::operator=(Base&&)

顯示單個Base實例已移動分配給兩次,一次由AB每個移動賦值運算符分配。 第二個移動分配來自已經從對象移動,這可能導致第一個移動分配的內容被覆蓋。

請注意,clang也會在我的示例中生成警告,它可能會檢測到示例中的兩個路徑無法訪問A並且不會發出警告。

修復它的方法是為AB實現移動賦值運算符,它可以檢測Base已被移動並省略第二個移動賦值。

暫無
暫無

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

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