默认模板类参数混淆了g ++?

and*_*ykx 9 c++ gcc templates g++ intel

昨天我遇到了一个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>() );
};
Run Code Online (Sandbox Code Playgroud)

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)'
Run Code Online (Sandbox Code Playgroud)

显然,当以这种方式编写时,不接受默认参数,并且编译器假定指定了新的函数参数而不是第二个模板参数,然后它需要一个默认值,因为stuff参数有一个.我可以通过创建一个typedef来帮助编译器,然后一切编译都很好:

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

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

所以我可以解决我的问题,但我不明白发生了什么.我在这里是否错过了C++(模板?)语言功能,我做错了什么,或者g ++编译器错误地接受了第一段代码?

注意BTW,这也编译...

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

void method ( Foo<int,int> const& stuff = Foo<int,int>() );
Run Code Online (Sandbox Code Playgroud)

Ric*_*den 12

我不太确定这是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>() ) );
};
Run Code Online (Sandbox Code Playgroud)

IMO,这些括号是必要的,因为还有一个要求,即默认参数可以引用类的成员,该成员可以在类体中稍后声明:

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

上面的代码是合法的,当正在解析'方法'的声明时,尚未看到成员'j'.因此,编译器只能使用语法检查来解析默认参数(即匹配括号和逗号).当g ++解析你的原始声明时,它实际看到的是以下内容:

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

添加额外的括号集可确保正确处理默认参数.

以下案例显示了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;
};
Run Code Online (Sandbox Code Playgroud)

编辑:

在回应评论时:"你也可以在那里进行带有多个参数的函数调用",这不会导致问题的原因是函数调用中的逗号包含在括号中:

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

因此,可以使用表达式的语法来解析它.在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;
};
Run Code Online (Sandbox Code Playgroud)

上面的"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)基类的成员,或

这里的子弹是强制编译器"延迟"查找默认参数含义的部分,直到声明了所有类成员为止.

<UPDATE>

正如"Employed Russian"所指出的那样,g ++ 4.4现在能够解析所有这些例子.但是,在C++标准委员会处理DR之前,我还没准备好将其称为"错误".我相信需要长期的额外括号来确保其他编译器/工具(甚至可能是g ++的未来版本)的可移植性.

根据我的经验,C++标准并没有规定编译器供应商都应该使用相同的解析器技术,他们也不能指望所有技术同样强大.因此,解析要求通常不要求供应商执行超人的专长.为了说明这一点,请考虑以下两个例子:

typedef T::TYPE TYPE;
T::TYPE t;
Run Code Online (Sandbox Code Playgroud)

如果'T'是依赖的,那么给定每个上下文'TYPE' 必须是一个typename,但是标准仍然需要typename关键字.这些示例是明确的,只能表示一件事,但标准(为了允许所有解析器技术)仍然需要typename关键字.

只要额外的括号允许代码解析,DR可能以这样的方式处理,即无法解析这些示例的编译器仍将是"标准符合".

</ UPDATE>