微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c – 默认模板类参数混淆g?

昨天我遇到一个g(3.4.6)编译器问题,我一直使用英特尔(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)'

显然,以这种方式写入认参数是不被接受的,而编译器则认为不是第二个模板参数,而是指定一个新的函数参数,因为该参数期望一个认值,因为该参数有一个参数.我可以通过创建一个typedef来帮助编译器,然后一切都编译好:

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

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

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

注意BTW,这也编译…

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

void method ( Foo<int,int>() );

解决方法

我不太确定这是g中的一个bug(从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) ); // 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”中定义,而不是模板参数列表的开头.再次,Comeau在上述代码生成语法错误,g(包括3.2.3)正确解析. FYI,适当的参考文献是8.3.6 / 5中的注释:

[Note: in member function declarations,names in default argument expressions are looked
up as described in 3.4.1…

然后在3.4.1 / 8

A name used in the deFinition of a member function (9.3) of class X following the function’s declaratorid29)
shall be declared in one of the following ways:

— shall be a member of class X or be a member of a base class of X (10.2),or

这里的子弹是强制编译器在认参数的意义上“延迟”查找,直到所有的类成员都被声明为止.

< UPDATE>

正如“已经雇用的俄罗斯人”所指出的,现在可以解释所有这些例子.不过,直到07年才被C标准委员会解决,我还没准备好把这个称作“错误”.我认为需要长期额外的括号才能确保对其他编译器/工具的可移植性(甚至是g的未来版本).

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

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

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

可能的是,DR可能被解决,使得无法解析这些示例的编译器仍将是“标准符合”,只要额外的括号允许代码解析.

< / UPDATE>

原文地址:https://www.jb51.cc/c/111183.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐