9

昨日、Intel(9.0)コンパイラを使用して問題なくコンパイルしているコードでg ++(3.4.6)コンパイラの問題が発生しました。何が起こったかを示すコードスニペットは次のとおりです。

template<typename A, typename B>
class Foo { };

struct Bar {
   void method ( Foo<int,int> const& stuff = Foo<int,int>() );
};

g++コンパイラエラーは次のとおりです。

foo.cpp:5: error: expected `,' or `...' before '>' token
foo.cpp:5: error: wrong number of template arguments (1, should be 2)
foo.cpp:2: error: provided for `template<class A, class B> struct Foo'
foo.cpp:5: error: default argument missing for parameter 2 of `void Bar::method(const Foo<int, int>&, int)'

明らかに、このように記述された場合、デフォルトの引数は受け入れられません。コンパイラは、2番目のテンプレート引数の代わりに新しい関数引数が指定されていると想定しますstuff。引数にはデフォルト値があるため、デフォルト値が必要です。typedefを作成することでコンパイラを支援できます。そうすれば、すべてが正常にコンパイルされます。

template<typename A, typename B>
class Foo { };

struct Bar {
   typedef Foo<int,int> FooType;
   void method ( FooType const& stuff = FooType() );
};

だから私は自分の問題を解決することはできますが、何が起こっているのかわかりません。ここでC++(テンプレート?)言語機能を見逃していて、何か間違ったことをしているのでしょうか、それとも最初のコードを受け入れないという点でg ++コンパイラが間違っているのでしょうか?

ところで、これもコンパイルされることに注意してください...

template<typename A, typename B>
class Foo { };

void method ( Foo<int,int> const& stuff = Foo<int,int>() );
4

4 に答える 4

12

これがg++のバグであるかどうかはわかりません(バージョン4.2.4以降)。コードはg++4.4で渡されるようになりました(以下のUPDATEを参照)。このコードを他のバージョンのコンパイラー用にコンパイルするために、デフォルトの引数の周りに括弧のセットを追加できます。

template<typename A, typename B>
class Foo { };

struct Bar {
  void method ( Foo<int,int> const& stuff = ( Foo<int,int>() ) );
};

IMO、これらの括弧は、デフォルトの引数がクラス本体の後半で宣言される可能性のあるクラスのメンバーを参照できるという追加の要件があるため、必要です。

struct Bar {
  void method ( int i = j);  // 'j' not declared yet
  static const int j = 0;
};

上記のコードは正当であり、「メソッド」の宣言が解析されているとき、メンバー「j」はまだ表示されていません。したがって、コンパイラは、構文チェックのみを使用してデフォルトの引数を解析できます(つまり、括弧とコンマの一致)。g ++が元の宣言を解析しているとき、実際に表示されているのは次のとおりです。

void method ( Foo<int,int> const& stuff = Foo<int // Arg 1 with dflt.
              , int>() );                         // Arg 2 - syntax error

括弧のセットを追加すると、デフォルトの引数が正しく処理されます。

次のケースは、g ++が成功したが、Comeauが構文エラーを生成する例を示しています。

template<int J, int K>
class Foo { };

struct Bar {
  void method ( Foo<0, 0> const & i = ( Foo<j, k> () ) );
  static const int j = 0;
  static const int k = 0;
};

編集:

「複数の引数を持つ関数呼び出しを使用することもできます」というコメントに応えて、これで問題が発生しない理由は、関数呼び出し内のコンマが括弧で囲まれているためです。

int foo (int, int, int);
struct Bar {
  void method ( int j =
                    foo (0, 0, 0) ); // Comma's here are inside ( )
};

したがって、式の構文のみを使用してこれを解析することが可能です。C ++では、すべての'('は')'と一致する必要があるため、これは簡単に解析できます。ここで問題が発生する理由は、「<」はC ++でオーバーロードされているため、一致する必要がないため、より小さい演算子またはテンプレート引数リストの先頭になる可能性があるためです。次の例は、デフォルトの引数で「<」が使用されている場所を示しており、より小さい演算子を意味します。

template<int I, int J>
class Foo { };

struct Bar {
  template <typename T> struct Y { };

  void method ( ::Foo<0,0> const& stuff = Foo<10 , Y < int >  = Y<int>() );

  struct X {
    ::Foo<0, 0> operator< (int);
  };

  static X Foo;
};

上記の「Foo<10」は、「X」で定義された「operator <」の呼び出しであり、テンプレート引数リストの先頭ではありません。繰り返しますが、Comeauは上記のコードで構文エラーを生成し、g ++(3.2.3を含む)はこれを正しく解析します。

参考までに、適切な参照は8.3.6/5の注記です。

[注:メンバー関数宣言では、デフォルトの引数式の名前は3.4.1で説明されているように検索されます。

そして3.4.1/8で

関数のdeclaratorid29)に続くクラスXのメンバー関数(9.3)の定義で使用される名前は、次のいずれかの方法で宣言する必要があります。

..。

—クラスXのメンバーであるか、X(10.2)の基本クラスのメンバーであるか、または

ここでのこの箇条書きは、すべてのクラスメンバーが宣言されるまで、コンパイラにデフォルト引数の意味のルックアップを「遅延」させる部分です。

<更新>

「EmployedRussian」で指摘されているように、g++4.4はこれらすべての例を解析できるようになりました。ただし、DRがC ++標準委員会によって対処されるまで、私はこれを「バグ」と呼ぶ準備ができていません。他のコンパイラ/ツール(そしておそらくg ++の将来のバージョン)への移植性を確保するために、長期的な追加の括弧が必要になると思います。

私の経験では、C ++標準では、コンパイラベンダーはすべて同じパーサーテクノロジを使用する必要があり、すべてのテクノロジが同等に強力であるとは期待できません。その結果、解析要件では通常、ベンダーが超人的な偉業を実行する必要はありません。これを説明するために、次の2つの例を検討してください。

typedef T::TYPE TYPE;
T::TYPE t;

「T」が依存している場合、各コンテキストで「TYPE」はタイプ名である必要がありますが、標準では引き続きtypenameキーワードが必要です。これらの例は明確であり、1つのことしか意味しませんが、標準(すべてのパーサーテクノロジーを可能にするため)では、typenameキーワードが必要です。

これらの例の解析に失敗したコンパイラが、追加の括弧でコードの解析が可能である限り、「標準準拠」であるようにDRがアドレス指定される可能性があります。

</ UPDATE>

于 2009-05-12T08:38:08.670 に答える
4

これはgccの既知のバグです。これはgcc-4.4で修正されており、元のソースを適切にコンパイルします。

于 2009-05-12T19:50:20.117 に答える
2

コンパイラのバグのようです。IBMのxlCコンパイラV7.0(gccよりも標準に準拠していることがわかりました)で試してみましたが、正常にコンパイルされます。

于 2009-05-12T07:55:37.210 に答える
-2
template <bool> class A {};
typedef A<static_cast<bool>(1>0)> B;//buggy
int main() { B b; }
于 2009-08-15T15:08:29.097 に答える