2017-06-14 2 views
22

何らかの理由で、GCCとclangの最新バージョンでは、この特定のシナリオで戻り値の共分散を認識しません。エラーメッセージは誤解を招くです:ここでは共変リターン型が認識されない

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()'は 't *'ではなく 'foo_default_impl *'を返す必要があります。 –

+2

以前のバージョンでコードをコンパイルしましたか?私はこれがfoo_default_implに渡されたときに 'derived'がまだ完全な型ではないのではないかと思います:http://eel.is/c++draft/class.derived#class.virtual-8 – marcinj

+0

@KhouriGiordano:なぜ?私たちが検討している特別なケースでは、 'T'は' derived'、 'derived'は' base'から公に由来します。 –

答えて

19

のことだ。ここでは

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 *') 

はコードがあります。私たちにはコンパイラが問題の型について知る必要があるすべてを知っているように見えるかもしれませんが、標準ではそうではないと言います。

[temp.arg.type/2]

は、... [注:テンプレート型引数が不完全型であってもよいです。 - エンドノート]

[basic.types/5]

宣言しかし定義されていない、 特定のコンテキスト([dcl.enum])で列挙型、または未知のバウンドの配列されたクラスまたは 不完全な要素タイプの、が不完全に定義されたオブジェクト・タイプである 0.46 不完全に定義されたオブジェクトタイプおよびCVボイド不完全型である ([basic.fu ndamental])。オブジェクトは、 不完全な型を持つように定義されてはならない。

[class/2]

クラス名、クラス名を見た直後に、それが 宣言されたスコープ内に挿入されます。クラス名も で、クラス自体のスコープに挿入されています。これは 注射クラス名として知られています。アクセスチェックの目的のために、 注射クラス名は、パブリックメンバー名であるかのように扱われます。 クラス指定子は、一般にクラス定義と呼ばれます。 クラス は、クラス指定子の閉じ括弧 が、と見なされ、そのメンバー関数は一般的にはまだ ではありませんが、定義されていると見なされます。オプションのattribute-specifier-seqはクラスに属します。 属性指定子seqの属性は、その後に、名前が付けられたときは常に とみなされます。

太字のテキストは、問題のコンパイラがタイプパラメータTを不完全オブジェクトタイプとして扱うという単純な絵を描いています。彼らは、この前方宣言がbaseから派生したクラスであることを推測することはできません

class derived; 

:そうのようにあなたが前方にのみ、それを宣言しているようなものです。したがって、彼らはfoo_default_implのコンテキストでそれを共変種戻り型として受け入れることはできません。等@marcinjin the commentsによって指摘された:

[class.virtual/8]

D :: Fの共変戻り型のクラスタイプは、そのBの :: Fと異なる場合、 Dの戻り型のクラスタイプ:: fが:: Dの宣言の時点でF 完全でなければならないか、Tであるのでクラス タイプD.

なければなりませんどちらも完全ではなく、foo_default_impl<T>そのものでもありません。共変種の復帰型ではありません。

+0

私は同意します。エラーメッセージ_( 'derived *'は 'base *'から派生したものではありません)の部分は、コンパイラがこのコードと混同していると思いました。 –

+2

幻想的な答えで素晴らしい質問。今日私は学んだ。 – cdhowie

+0

'sizeof(complete_type)'がうまくいかなければならないという上記のコメントの要点は、なぜ 'derived'がその行で完結できないのかを明確にしています。私たちは 'derived'の基底を定義しています、' derived'がどのくらい大きいかをどのように知ることができますか? – Yakk

関連する問題