昨天我遇到了一个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>() );
答案 0 :(得分: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>() ) );
};
IMO,这些括号是必要的,因为还有一个要求,即默认参数可以引用类的成员,该成员可以稍后在类体中声明:
struct Bar {
void method ( int i = j); // 'j' not declared yet
static const int j = 0;
};
上面的代码是合法的,当正在解析'method'的声明时,尚未看到成员'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 ++中,所有'('必须与'匹配'',因此这很容易解析。这里出现问题的原因是'&lt;'不需要匹配,因为它在C ++中重载,因此可以是小于运算符或模板参数列表的开头。以下示例显示'&lt;'的位置在默认参数中使用并隐含小于运算符:
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&lt; 10”是对“运营商&lt;”的调用在'X'中定义,而不是模板参数列表的开头。同样,Comeau会在上面的代码中生成语法错误,而g ++(包括3.2.3)会正确地解析它。
仅供参考,适当的参考资料是8.3.6 / 5中的注释:
[注意:在成员函数声明中,查看默认参数表达式中的名称 如3.4.1 ...
中所述
然后在3.4.1 / 8
在函数的declaratorid29之后的类X的成员函数(9.3)的定义中使用的名称 应以下列方式之一宣布:
...
- 应该是X类成员或者是X(10.2)基类的成员,或者
这里的子弹是强制编译器“延迟”查找默认参数含义的部分,直到声明了所有类成员为止。
&lt; UPDATE&gt;
正如“雇用俄语”所指出的那样,g ++ 4.4现在能够解析所有这些例子。但是,在C ++标准委员会已经解决DR之前,我还没准备好将其称为“错误”。我相信需要长期的额外括号来确保其他编译器/工具的可移植性(甚至可能是g ++的未来版本)。
根据我的经验,C ++标准并没有规定编译器供应商都应该使用相同的解析器技术,他们也不能指望所有技术同样强大。因此,解析要求通常不要求供应商执行超人的专长。为了说明这一点,请考虑以下两个例子:
typedef T::TYPE TYPE;
T::TYPE t;
如果'T'是依赖的,那么给定每个上下文'TYPE'必须是一个类型名称,但标准仍需要 typename 关键字。这些示例是明确的,只能表示一件事,但标准(为了允许所有解析器技术)仍然需要 typename 关键字。
只要额外的括号允许代码解析,可能会以这样的方式处理DR,使得无法解析这些示例的编译器仍然是“标准符合”。
&lt; / UPDATE&gt;
答案 1 :(得分:4)
这是gcc中已知的bug。 它已在gcc-4.4中修复,它可以很好地编译原始源。
答案 2 :(得分:2)
看起来像编译器错误。我在IBM的xlC编译器V7.0上尝试过它(我发现它比gcc更符合标准)并且编译好了。
答案 3 :(得分:-2)
template <bool> class A {};
typedef A<static_cast<bool>(1>0)> B;//buggy
int main() { B b; }