2017-06-14 65 views
22

由於某些原因,GCC和clang的最新版本無法識別此特定場景中的返回類型協方差。該錯誤消息是誤導:Covariant返回類型不被識別

error: return type of virtual function 'foo' is not covariant with the return 
    type of the function it overrides ('derived *' is not derived from 'base *') 

下面是代碼:

class base 
{ 
private: 
    virtual base * foo() = 0; 
}; 

template< class T > 
class foo_default_impl : public virtual base 
{ 
private: 
    T * foo() override { return nullptr; } 
}; 

class derived : public virtual base, private foo_default_impl<derived> 
{ 
}; 

int main() { 
    derived d{}; // error: return type of virtual function 'foo' is not covariant with the return type of the function it overrides ('derived *' is not derived from 'base *') 
    return 0; 
} 
+0

'foo()'需要返回'foo_default_impl *',而不是'T *'。 –

+2

以前的版本是否會編譯你的代碼?我不知道這是因爲'derived'在傳遞給foo_default_impl時還不是一個完整的類型:http://eel.is/c++draft/class.derived#class.virtual-8 – marcinj

+0

@KhouriGiordano:爲什麼?在我們正在考慮的特定情況下,'T'將會是'derived',並且'derived'是從'base'公開派生的。 –

回答

19

事情是這樣的。儘管對我們來說,編譯器可能知道需要了解的所有類型,但標準另有說明。

[temp.arg.type/2]

... [注:模板類型參數可以是一個不完整的類型。 - 注完]

[basic.types/5]

已聲明但沒有定義,A類在 某些上下文中的枚舉類型([dcl.enum]),或未知邊界的陣列或不完全 元素類型的,是未完全定義的對象類型 0.46 未完全定義的對象類型和CV空隙是不完全類型 ([basic.fu ndamental])。對象不應被定義爲具有不完整類型 。

[class/2]

一個類名稱被插入到在其中聲明 的類名被看作後立即範圍。類名也是 插入到類本身的範圍內;這被稱爲 注入類的名稱。爲了進行訪問檢查, 注入類名稱被視爲是公共成員名稱。 A類別說明符通常稱爲類定義。 類別 被認爲是在其類別說明符 的大括號已被看到之後定義的,儘管其成員函數一般尚未定義爲 。可選的attribute-specifier-seq屬於類; attribute-specifier-seq中的屬性之後是 ,只要它被命名,就會考慮該類的屬性。

粗體塗料中的文本的簡單圖像所討論的編譯器處理類型參數T作爲一個不完整的對象類型。就好像你只向前宣佈它,就像這樣:

class derived; 

他們不能推斷出這個前置聲明是base派生的類。因此,他們不能接受它作爲foo_default_impl上下文中的協變類型返回類型。指出像出由@marcinjin the comments

[class.virtual/8]

如果d :: f的協變返回類型的類類型從 該B的::˚F不同,在d :: f的返回類型類類型應 完整的d ::的F申報點或應爲類 型D.

由於T是既不完整,也不是foo_default_impl<T>本身,它不能是一個協變式返回類型。

+0

我同意。部分錯誤消息_('derived *'不是從'base *'派生的)_讓我覺得編譯器與這段代碼混淆了。 –

+2

夢幻般的問題與一個夢幻般的答案。今天我學到了。 – cdhowie

+0

上述評論中提到的觀點 - 'sizeof(complete_type)'必須工作 - 也清楚說明爲什麼'derived'不能在該行完成。我們正在定義'derived'的基礎,我們怎麼能知道'derived'是多大? – Yakk