6

どして

class A;
template<typename T> class B
{
private: 
    A* a;

public:  
    B();
};


class A : public B<int>
{
private:    
    friend B<int>::B<int>();
    int x;
};


template<typename T>
B<T>::B()
{
    a = new A;
    a->x = 5;
}

int main() { return 0; }

結果

../src/main.cpp:15:エラー:テンプレートとしてのコンストラクターの無効な使用
../src/main.cpp:15:注:「B::classB」の代わりに「B::B」を使用してコンストラクターに修飾名を付けます

まだ結果にfriend B<int>::B<int>()変更friend B<int>::B()

../src/main.cpp:15:エラー:いいえ'void B :: B()'クラス'B'で宣言されたメンバー関数</p>

テンプレートを完全に削除しながら

class A;
class B
{
private:
    A* a;

public:
    B();
};


class A : public B
{
private:
    friend B::B();
    int x;
};


B::B()
{
    a = new A;
    a->x = 5;
}

int main() { return 0; }

コンパイルと実行は問題なく実行されます-私のIDEが友人B::B()は無効な構文であると言っているにもかかわらず?

4

4 に答える 4

5

CWG欠陥147の解決策(解決策はC ++ 03に組み込まれました)によると、クラステンプレート特殊化の非テンプレートコンストラクターに名前を付ける正しい方法は次のとおりです。

B<int>::B();

ではなく

B<int>::B<int>();

後者が許可されている場合、クラステンプレートの特殊化のコンストラクターテンプレートの特殊化がある場合、あいまいさがあります。2つ目<int>は、クラステンプレートまたはコンストラクターテンプレートのどちらですか。(詳細については、上記のリンク先の欠陥レポートを参照してください)

したがって、クラステンプレート特殊化のコンストラクターをフレンドとして宣言する正しい方法は次のとおりです。

friend B<int>::B();

Comeau4.3.10.1とIntelC++11.1はどちらもその形式を受け入れます。Visual C++2008もVisualC++ 2010もそのフォームを受け入れませんが、どちらも(誤った)フォームを受け入れfriend B<int>::B<int>();ます(Microsoft Connectに欠陥レポートを提出します)。

gccは、バージョン4.5より前のどちらの形式も受け入れません。 バグ5023はgcc3.0.2に対して報告されましたが、バグレポートで要求された解決策は無効な形式でした。バグ9050の解決によってこの問題も解決され、gcc4.5は正しいフォームを受け入れるようです。Georg Fritzscheは、質問へのコメントでこれを確認しました。

于 2010-05-13T01:52:43.473 に答える
1

そして、後者の場合、IDEがフレンドB :: B()を無効な構文として表示する理由は何ですか?IDEエラー。

アップグレードできない場合のテンプレートケースのgccで見つけた回避策は、B()の実装をメンバー関数void B :: init()に移動し、代わりに友情を付与することです。これでIDEもシャットダウンされると思います。

ただし、これをコンパイルしても、Bをインスタンス化しようとするとすぐに、スタックオーバーフローの問題が発生します。

于 2010-05-13T02:06:50.007 に答える
1

typedefは役に立ちませんか?例えば

class A : public B<int>
{
    typedef B<int> Base;   
    friend Base::Base();
    int x;
};

編集:C ++ 0xの最終委員会草案には、セクション3.4.3.1[ class.qual ]に次の言語が含まれています。

コンストラクターが許容可能なルックアップ結果であり、ネストされた名前指定子がクラスCを指定するルックアップでは、ネストされた名前指定子の後に指定された名前が、 Cでルックアップされたときに、C(第9項)、またはnested-name-specifierの後に指定された名前が、nested-name-specifier最後のコンポーネントの識別子またはsimple-template-idtemplate-nameと同じである場合、その名前代わりに、クラスCのコンストラクターに名前を付けると見なされます。

ネストされた名前指定子( )のBase後に指定された名前()は、ネストされた名前指定子の最後のコンポーネントの識別子と同じであるように聞こえるので、このコードはコンストラクターに名前を付けます。Base::

しかし、これをセクション12.1[ class.ctor ]と一致させることはできません。

コンストラクターには名前がないため、名前の検索中にコンストラクターが検出されることはありません。

まあ、本当に?3.4.3.1のその言語はどのように機能しますか?

typedef-nameは、コンストラクター宣言のdeclarator-idクラス名として使用してはなりません。

段落1ではネストされた名前指定子、、、およびが除外friendされているため、セクション12.1ではコンストラクターの導入宣言のみが説明されているように見えることを除けば、これはかなり明確に思えますusing。友達の宣言に適用される場合は、禁止されているようにも見えますfriend Base::B();

関数指定子(7.1.2)のオプションのシーケンス、コンストラクターのクラス名、パラメーターリストを使用する特別な宣言子構文を使用して、コンストラクターを宣言または定義します。

これらの関数指定子inline、、、virtualexplicitあり、コンストラクターはとにかくできませvirtualん。

于 2010-05-13T02:08:44.130 に答える
0

私はあなたを推測します;友人のテンプレート化されたコンストラクターでここで癖の領域に入る方法です。これはVS2010で正常にコンパイルおよび実行されますが、のデフォルトコンストラクターがデフォルトコンストラクターをA呼び出し、そのデフォルトコンストラクターが再度Bインスタンス化されると、スタックオーバーフローが発生しますA

于 2010-05-13T00:51:54.297 に答える