Я не уверен, что это ошибка в 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 может быть адресован таким образом, что компилятор, который не сможет проанализировать эти примеры, все равно будет "соответствовать стандарту", если дополнительная скобка позволяет анализировать код.