аргумент класса шаблона по умолчанию смущает g ++? - PullRequest
9 голосов
/ 12 мая 2009

Вчера я столкнулся с проблемой компилятора g ++ (3.4.6) для кода, который я без проблем компилировал с помощью компилятора Intel (9.0). Вот фрагмент кода, который показывает, что произошло:

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

Очевидно, что аргумент по умолчанию не принимается при написании таким образом, и компилятор предполагает, что вместо второго аргумента шаблона указан новый аргумент функции, для которого он затем ожидает значение по умолчанию, потому что аргумент 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 ]

12 голосов
/ 12 мая 2009

Я не уверен, что это ошибка в g ++ (начиная с версии 4.2.4). Код теперь переходит на g ++ 4.4 (см. ОБНОВЛЕНИЕ ниже). Чтобы этот код компилировался для других версий компиляторов, вы можете добавить скобки вокруг аргумента по умолчанию:

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;
};

EDIT:

В ответ на комментарий: «С таким же успехом можно вызвать вызов функции с несколькими аргументами», причина того, что это не вызывает проблем, заключается в том, что запятая внутри вызова функции заключена в круглые скобки:

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

Поэтому возможно проанализировать это, используя синтаксис только выражения. В C ++ все '(' должно совпадать с ')', и это легко разобрать. Причина этой проблемы заключается в том, что «<» не нужно сопоставлять, поскольку оно перегружено в C ++ и поэтому может быть оператором «меньше» или началом списка аргументов шаблона. В следующем примере показано, где «<» используется в аргументе по умолчанию и подразумевает оператор «меньше чем»: </p>

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», а не началом списка аргументов шаблона. И снова Comeau генерирует синтаксические ошибки в приведенном выше коде, и g ++ (включая 3.2.3) правильно его анализирует. </p>

К вашему сведению, соответствующие ссылки - примечание в 8.3.6 / 5:

[Примечание: в объявлениях функций-членов ищутся имена в выражениях аргументов по умолчанию как описано в 3.4.1 ...

, а затем в 3.4.1 / 8

Имя, используемое в определении функции-члена (9.3) класса X после объявления функции ID29) декларируется одним из следующих способов:

...

- должен быть членом класса X или членом базового класса X (10.2) или

Этот пункт здесь - это часть, которая заставляет компилятор «задерживать» поиск значения аргумента по умолчанию до тех пор, пока не будут объявлены все члены класса.

Как указывает "Employed Russian", g ++ 4.4 теперь может анализировать все эти примеры. Однако до тех пор, пока комитет по стандартам C ++ не рассмотрит вопрос DR , я еще не готов назвать это «ошибкой». Я считаю, что для обеспечения переносимости с другими компиляторами / инструментами (и, возможно, даже с будущими версиями g ++) понадобятся долгосрочные дополнительные скобки.

По моему опыту, стандарт C ++ не предписывает, чтобы все производители компиляторов использовали одну и ту же технологию синтаксического анализа, и они также не могут ожидать, что все технологии одинаково мощны. В результате требования синтаксического анализа обычно не требуют от поставщиков выполнения сверхчеловеческих умений. Для иллюстрации рассмотрим следующие два примера:

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

Если 'T' является зависимым, то для каждого контекста 'TYPE' должно быть именем типа, однако стандарт все еще требует ключевое слово typename . Эти примеры однозначны и могут означать только одно, однако стандарт (для учета всех технологий синтаксического анализа) по-прежнему требует ключевое слово typename .

Возможно, что DR может быть адресован таким образом, что компилятор, который не сможет проанализировать эти примеры, все равно будет "соответствовать стандарту", если дополнительная скобка позволяет анализировать код.

4 голосов
/ 12 мая 2009

Это известная ошибка в gcc. Это было исправлено в gcc-4.4, который прекрасно компилирует исходный код.

2 голосов
/ 12 мая 2009

Похоже на ошибку компилятора. Я попробовал его на компиляторе IBM xlC V7.0 (который, как мне показалось, более соответствует стандарту, чем gcc), и он хорошо компилируется.

0 голосов
/ 15 августа 2009
template <bool> class A {};
typedef A<static_cast<bool>(1>0)> B;//buggy
int main() { B b; }
...