簡體   English   中英

菱形層次結構中的虛函數重載在 clang 和 gcc 中產生不同的結果

[英]Virtual function overloading in diamond hierarchy produces different results in clang and gcc

以下代碼在 clang 上產生不同的結果。

#include <iostream>

struct Dummy1 {};
struct Dummy2 {};

struct A {
    virtual void foo(Dummy1) {
        std::cout << "A" << std::endl;
    }
    virtual void foo(Dummy2) {
        std::cout << "A" << std::endl;
    }
};


template<class T>
struct C : virtual A {
    using A::foo;
    void foo(Dummy2) override {
        std::cout << "C" << std::endl;
    }   
};


template<class T>
struct B : virtual A {
    using A::foo;
    void foo(Dummy1) final {
        std::cout << "B" << std::endl;
    }   
};

template<class T>
struct D : B<T>, C<T> {
    // using B<T>::foo; // error: call to member function 'foo' is ambiguous
    // using C<T>::foo; // error: call to member function 'foo' is ambiguous
    using A::foo;
};


int main() {
    D<int> d;
    d.foo(Dummy1{});
    d.foo(Dummy2{});

    A& a = d;
    a.foo(Dummy1{});
    a.foo(Dummy2{});

    B<int>& b = d;
    b.foo(Dummy1{});
    b.foo(Dummy2{});


    C<int>& c =d;
    c.foo(Dummy1{});
    c.foo(Dummy2{});

    return 0;
}

gcc(版本 4.8.1 - 9.1),icc(版本 16、17、19),Visual Studio 2017 15.4.0 Preview 1.0,Visual Studio 2013 12.0.31101.00 Update 4,clang(版本 3.4.1 - 3)。

全部給出以下輸出,這是我所期望的:

B
C
B
C
B
C
B
C

僅選擇方法C<T>::foo(Dummy1)B<T>::foo(Dummy2) ,不使用方法A<T>::foo

Clang(版本 4.0.0 - 8.0.0)在通過D<T>對象調用時選擇A::foo(Dummy2) ,然后才選擇。 當通過引用B<T>調用時 - C<T>::foo(Dummy2)被選中。

B
A <-- difference
B
C
B
C
B
C

當派生類的順序更改為struct D : C<T>, B<T> ,輸出更改為:

A <--
C
B
C
B
C
B
C

似乎對於第二個派生類方法foo不被認為是虛擬的。

只有 Visual Studio 發出警告,而不是有用的C4250

using B<T>::foo;編寫using B<T>::foo; using C<T>::foo; D<T>而不是using A::foo; 使 clang 產生以下錯誤:

錯誤:對成員函數“foo”的調用不明確

在 gcc 上的行為不會改變,代碼編譯和輸出是相同的。

這里的正確行為是什么?

由於應用程序給出了不同的結果,有沒有辦法找到這種結構的所有相似實例或采取一些解決方法? 我必須同時使用 gcc 和 clang 進行編譯。 檢查相同的問題是否存在於比我發現的更多的地方可能很困難。

我相信這里發生了兩件事。

首先,clang 的實現肯定存在問題,因此提交錯誤報告是一個很好的舉措。

但是當你在D引用上調用 foo 時也有一個問題

D<int> d;
d.foo(Dummy1{});
d.foo(Dummy2{});

Visual Studio 為您提供的警告實際上非常准確:從技術上講,每次調用中有兩個選項,但通過優勢選擇一個而不是另一個。

對於Dummy1重載,類B有被覆蓋的實現,還有來自類C未被覆蓋的實現。 這就是警告所說的“主要”一個隱藏另一個。 在這種情況下,主要版本是B類中的覆蓋版本,而弱版本是C類中的版本。 如果您還覆蓋了C類中的Dummy1重載,您就會有一個模棱兩可的調用。

Dummy2過載也類似。

所以編譯器警告你的是,在已知D實例的情況下,你實際上有一個選擇,應該明確地說明它。

暫無
暫無

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

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