最令人烦恼的解析是否根植于是否使用void
作为不带参数的函数声明的参数的歧义?
例如,以下代码在g ++(v7.2.1)和Xcode(Apple LLVM版本7.0.2(clang-700.1.81))编译器上编译时没有错误,运行正常。
#include <iostream>
int asdf(void);
int asdf(int a) {
return a;
}
int main() {
std::cout << asdf(6) << std::endl; //-> 6
return 0;
}
这一直回到ANSI-C标准之前,其中空参数列表表示任意数量的参数。它今天困扰着我们,在编译器中具有向后兼容性,这似乎对这个问题有点困惑。如果上述不至少产生警告,如果没有抛出错误?
在这里,我有一个顿悟(或许是一个白日梦!)。可能是最令人烦恼的解析根源于在空函数声明列表中使用void
的模糊性吗?
另一个例子,引用以下维基百科示例:
class Timer {
public:
Timer();
};
class TimeKeeper {
public:
TimeKeeper(const Timer& t);
int get_time();
};
int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
该行
TimeKeeper time_keeper(Timer());
似乎含糊不清,因为它可以解释为
- 类TimeKeeper的变量time_keeper的变量定义,使用类Timer或
的匿名实例初始化- 函数time_keeper的函数声明,它返回一个TimeKeeper类型的对象,并且有一个(未命名的)参数, 是指向函数返回类型Timer的指针(并且没有输入)。 (参见函数对象#In C和C ++)
参考: https://en.wikipedia.org/wiki/Most_vexing_parse
现在,如果我们在声明一个没有变量的函数时指定必须使用void
,那么这不会消除(不是hackishly,从根本上删除!)歧义吗?有问题的行将成为以下内容,毫无疑问它是一个函数声明:
int main() {
TimeKeeper time_keeper(Timer(void));
return time_keeper.get_time();
}
这一直是回到KnR,讨论了这个问题:
由于getline和copy的专用版本没有参数, 逻辑会建议他们的原型在文件的开头 应为
getline()
和copy()
。但为了兼容性 旧的C程序标准将空列表作为旧式 声明,并关闭所有参数列表检查;这个单词void
必须用于显式空列表。 [Kernighan&amp; Richie,C编程语言,1988,Pgs 32-33]
和..
空参数列表的特殊含义是允许的 使用新编译器编译的旧C程序。但这不是一个坏主意 将它与新程序一起使用。如果函数接受参数,则声明 他们;如果没有参数,请使用void [同上,Pg。 73]
答案 0 :(得分:2)
函数定义也是一个声明。这意味着什么时候有
int asdf(void);
int asdf(int a) {
return a;
}
您已宣布两个asdf
版本,一个版本为int
而另一个版本为“{1}}”。然后,在使用
int
版本
std::cout << asdf(6) << std::endl; //-> 6
由于定义了int
版本,因此没有任何问题。
如果您尝试使用
,会出现什么问题asdf();
当你这样做,因为它已被声明但未定义你应该得到
未定义对`asdf()&#39;
的引用
由于没有定义。这与最令人烦恼的解析无关,而只是函数声明和定义之间的区别。
答案 1 :(得分:2)
您的提案不会完全消除歧义。
考虑:
#include <string>
struct Foo { ... };
int main(int argc, char **argv) {
Foo bar(std::string(argv[1]));
}
这似乎可以声明类型为bar
的局部变量Foo
,并将std::string
传递给其构造函数。
但实际上它意味着:
Foo bar(std::string *argv);
即。声明bar
作为一个函数,使用“std::string
指针”类型的参数并返回Foo
。
无法通过添加void
修复此示例。