GCC的两个异常错误消息

3

今天,我遇到了两个之前从未见过的错误信息,这对我来说完全是新鲜事。

以下是代码:

template<typename T>
struct adder { adder(const T &item) { } };

template<typename T>
void initializer(const T &item) {  adder<T>(item); }

int main() 
{
   initializer("const string literal");
}

在编译时,GCC会给出以下错误: prog.cpp: In function ‘void initializer(const T&)’: prog.cpp:6: error: declaration of ‘adder<T> item’ shadows a parameter prog.cpp: In function ‘void initializer(const T&) [with T = char [21]]’: prog.cpp:10: instantiated from here prog.cpp:6: error: declaration of ‘adder<char [21]> item’ shadows a parameter prog.cpp:6: error: no matching function for call to ‘adder<char [21]>::adder()’ prog.cpp:3: note: candidates are: adder<T>::adder(const T&) [with T = char [21]] prog.cpp:3: note: adder<char [21]>::adder(const adder<char [21]>&
看到粗体文字。一个错误被显示两次,即:
error: declaration of ‘adder<T> item’ shadows a parameter error: declaration of ‘adder<char [21]> item’ shadows a parameter 这是什么意思?为什么它用不同的模板参数显示两次?第一个使用了T,第二个使用了char [21]
编辑:是否adder<T>(item)声明了名为item的变量?但这不是我的意图。我认为它应该创建一个将item作为参数传递给构造函数的临时对象。 我想知道处理此问题的标准部分! 另一个有趣的错误是:
error: no matching function for call to ‘adder<char [21]>::adder()’
这表明编译器正在寻找默认构造函数?但我想知道为什么编译器在实际上我的代码在第6行没有使用它时会寻找它?
ideone上的代码:http://www.ideone.com/jrdLL

1
adder<T>(item) 的意思是 adder<T> item; - 然后这两个错误就有意义了。 - Erik
我编辑了我的回答,请查看。 - Didier Trosset
4个回答

4
这表明编译器正在寻找默认构造函数?但我想知道为什么编译器在第6行实际上没有使用它时会寻找它? 因为编译器认为你声明了名为item的局部变量。

http://codepad.org/YBPKCvmm


3
我目前只能获取C++0x草案,所以我无法给出当前的章节和具体内容,但我认为没有太大变化。 在0x中,它在6.8节——歧义解决中:

语法中存在一种模糊性,涉及表达式语句和声明:如果一个函数样式的显式类型转换(5.2.3)作为其最左边的子表达式的表达式语句与第一个声明符以一个括号开头的声明相似,则这个语句是一个声明。

[...]

T(a); // declaration

也就是说,声明了一个名为“a”的变量,类型为T。

如果您的adder<T>(item)定义了一个临时(未命名)对象,则它将成为表达式语句,但如果某些内容既可以解析为声明语句也可以解析为表达式语句,则C ++将其解析为声明语句。

[...] 解决方法是将任何可能是声明的结构都视为声明。(8.2)

换句话说,这是每个人亲爱的老朋友“最令人烦恼的解析”。

更新: 我查看了C++03中的歧义解决方法,这些段落是相同的。


3
理解正在发生的事情的关键是要意识到: adder(item); 是一个名为item且类型为adder的本地变量定义;括号是多余的,但完全被允许。如果您想调用构造函数,您需要消除歧义,通过以某种方式编写它,使其不能被解释为数据定义,例如: adder((item)); (我不确定这可能有什么用。它构造了一个adder的临时对象,然后在表达式结束时析构它。)
一旦将语句理解为数据声明,实际的错误消息应该更清晰:函数参数被视为在函数的顶级块中定义,因此adder(item)是重复(并相互矛盾)的定义,并且adder没有默认构造函数,因此您无法在不提供参数的情况下定义它的实例。

1
关于同一个错误出现两次的事实:编译器会对模板定义进行两次(或更多次)解析。第一次是在它首次看到它时,然后每次实例化模板时都会进行解析。并非所有错误都可以在第一次解析期间检测到:编译器无法知道 adder<T> 没有默认构造函数,直到它知道 T。但是,它可以清楚地看到,无论 T 是什么,adder<T> 项都定义了一个与参数相同名称的变量,因此当它解析模板时发出错误,然后再尝试实例化它时再次发出错误。 - James Kanze
这很好。但我想知道根据哪个标准,adder<T>(item)是一个名为item的变量声明的部分。 - Nawaz
1
基本上,如果令牌序列可以解释为声明或表达式语句,则它是一个声明。当然,如果你只想声明一个adder<T>,通常不需要使用括号。但是它们是合法的(并且构造更复杂的类型时必需的)。所以这是一个声明。 - James Kanze

1
“影子变量”是指两个对象具有相同的名称,这在语言中是允许的,但可能不是预期的行为。

这是错误的。标准的§3.3.2规定:“函数定义的最外层块以及与函数try块相关联的任何处理程序的最外层块中不得重新声明参数名。” - James Kanze
我也希望编译器实现标准 :) - Simon Richter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接