2016-10-05 9 views
8

私はGCC 6.2.0だけではなく、Clang 3.9.0(両方とも-std=c++14モード)でしか現れないエラーに遭遇しました。私はどのような動作が正しいか(バグを報告する必要があるかどうか)は不明です。 GCCでフレンドクラス、継承、およびtypedef - 動作は正しいですか?

template<typename type_t> 
class foo_t 
{ 
}; 

class bar_t 
{ 
public: 
    using foo_t = int; 
}; 

class baz_t: 
    public bar_t 
{ 
private: 
    template<typename type_t> 
    friend class foo_t; 
}; 

これは、次のエラー得られます:ここで

コードだ、私はC++標準の知っていることから、

test.cpp:17:15: error: using typedef-name ‘using foo_t = int’ after ‘class’ 
    friend class foo_t; 
       ^~~~~ 
test.cpp:9:19: note: ‘using foo_t = int’ has a previous declaration here 
    using foo_t = int; 
       ^

を、親typedef年代(またはusing複数可)に漏れてはなりません子のスコープに入れて、明示的に名前を修飾する必要があります(例:Propagating 'typedef' from based to derived class for 'template'を参照)。 GCCはここでは間違っているようですが、C++の知識が自信を持って語ることはそれほど確かではありません。

ありがとうございました!

+1

面白い回避策: 'friend class :: foo_t'が動作します。 –

+0

':: foo_t'はグローバルスコープを意味するためです。 '::'がなければ、コンパイラは可能な限り最も近いスコープを処理します –

+0

誰かが興味を持っている場合 - 私はhttps://gcc.gnu.org/に適用された回避策で私の新しい例でICEに関するバグを提出しました。 bugzilla/show_bug.cgi?id = 77869 –

答えて

4

From what I know of C++ standard, parent typedef 's (or using s) should not leak into scope of the child and you need to explicitly qualify the name

これは間違っています。基本クラスで宣言されたメンバー(型エイリアスを含む)は、通常は派生クラスで可視です。ここでリンクした質問は、2フェーズの名前検索が適用される依存基本クラスを持つテンプレートを扱います(型エイリアスだけでなく、すべてに適用されます)。

これはさておき、規格の関連部分はC++ 14(N4140)dcl.type.elab] 7.1.6.3/2(強調鉱山):

3.4.4 describes how name lookup proceeds for the identifier in an elaborated-type-specifier. If the identifier resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the same way a simple-type-specifier introduces its type-name.If the identifier resolves to a typedef-name or the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed.

(注:が精緻-type-specifierは、私たちが扱っているのための構造class Tです)。

3.4.4は、elaborated-type-specifierの識別子を名前に解決するときに、型のない名前は無視されますが、型名は通常通り検出されます。

したがって、スコープbar_tのtypedef-name foo_tは、グローバルスコープのテンプレート名foo_tよりもスコープが「近い」ため、GCCは実際には正しいです。したがって、修飾されていない名前foo_tbaz_tになり、bar_t::foo_tに解決されます。これはtypedef-nameです。したがって、詳細な型指定子が不正になります。

問題は、の不適格なの名前foo_tの解決にあります。

tempalte <typename type_t> 
friend class ::foo_t; 
+0

ああ、私は依存ベースクラスについて、今、感謝しています(私は実際にそれらを最近遭遇し、誤って行動を外挿しました)。 –

+0

しかし、私は2番目の部分を理解していません - なぜ精巧な指定子がこの 'typedef'を使用できないのでしょうか? (これは当初、最初のコメントに入るはずだった、申し訳ありません) –

+0

@NikolayAmiantov申し訳ありませんが、私は答えを大幅に書き直しました(基本的には、最初から2番目の部分を書き直しました)。あなたの質問がまだ適用されているかどうかを確認できますか? – Angew

1

メンバーの宣言そのサブクラスに見えるDO:あなたは質問へのコメントで自分を注意として、明示的に問題を解決しなければならない意味どのfoo_t述べます。あなたの質問の問題は重複宣言です。次のコードを確認してください

template<typename type_t> 
class foo_t 
{ 
}; 

class bar_t 
{ 
public: 
    using foo_t = int; 
}; 

class baz_t : 
    public bar_t 
{ 
public: 
    foo_t val; // Here, the foo_t refers to the int 
}; 

int main() 
{ 
    baz_t().val; // this works 
} 

投稿したエラーコードtest.cpp:9:19: note: ‘using foo_t = int’ has a previous declaration hereは既にこの問題について言及しています。他の手に


、友人のクラスは、単純なデータ型に適用することはできません、次のコードをチェックしてください:ここでは

using foo_t = int; 

class bar_t 
{ 
public: 

}; 

class baz_t : 
    public bar_t 
{ 
public: 
    template<typename type_t> 
    friend class foo_t; 
    //error: using typedef-name ‘using foo_t = int’ after ‘class’ 
}; 

をあなたが本当に重複している必要がある場合は、回避策です宣言名:

template<typename type_t> 
class foo_t 
{ 
}; 

class bar_t 
{ 
public: 
    using foo_t = int; 
}; 

class baz_t: 
    public bar_t 
{ 
public: 
    template<typename type_t> 
    friend class ::foo_t; // The global scope 

    foo_t val; // The int 
}; 
+0

ご協力ありがとうございます!私は問題を示すために例として 'int'を指定しましたが、説明はとにかく役に立ちました。私は今、より重大な問題を抱えています(上記のコメントを参照)、それの要点を得るのに時間が必要です〜_ ^。 –

関連する問題