Does specifying the use of void in the declaration

2019-09-15 17:54发布

Is the Most Vexing Parse rooted in the ambiguity about whether or not to use void as the parameter of a function declaration that takes no arguments?

As an example, the following code compiles without error, and runs fine, on both the g++ (v7.2.1) and Xcode (Apple LLVM version 7.0.2 (clang-700.1.81)) compilers.

#include <iostream>

int asdf(void);
int asdf(int a) {
    return a;
}
int main() {
    std::cout << asdf(6) << std::endl; //-> 6
    return 0;
}

This goes all the way back to the ANSI-C standard and before, where an empty parameter list indicated an arbitrary number of arguments. It haunts us today with backward-compatibility in the compilers, that seem to be a bit confused about the issue. Should the above not at least generate a warning, if not throw an error?

Here I had an epiphany (or perhaps a pipe dream!). Could it be that The Most Vexing Parse is rooted in the ambiguity about the use of void in an empty function declaration list?

Another example, Referencing the following Wikipedia example:

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();
}

The line

TimeKeeper time_keeper(Timer());

is seemingly ambiguous, since it could be interpreted either as

  • a variable definition for variable time_keeper of class TimeKeeper, initialized with an anonymous instance of class Timer or
  • a function declaration for a function time_keeper which returns an object of type TimeKeeper and has a single (unnamed) parameter which is a pointer to function returning type Timer (and taking no input). (See Function object#In C and C++)

REFERENCE: https://en.wikipedia.org/wiki/Most_vexing_parse

Now, if we specify that void must be used when declaring a function with no variables, does this not remove (not hackishly, fundamentally remove!) the ambiguity? The line in question would then become the following, leaving no doubt that it is a function declaration:

int main() {
  TimeKeeper time_keeper(Timer(void));
  return time_keeper.get_time();
}

This goes all the way back to KnR, where this issue is discussed:

Since the specialized versions of getline and copy have no arguments, logic would suggest that their prototypes at the beginning of the file should be getline() and copy(). But for compatibility with older C programs the standard takes an empty list as an old-style declaration, and turns off all argument list checking; the word void must be used for an explicitly empty list. [Kernighan & Richie, the C programming language, 1988, Pgs 32-33]

and..

The special meaning of the empty argument list is intended to permit older C programs to compile with new compilers. But it's a bad idea to use it with new programs. If the function takes arguments, declare them; if it takes no arguments, use void [ibid, Pg. 73]

Reference: is f(void) deprecated in modern C and C++

2条回答
Fickle 薄情
2楼-- · 2019-09-15 18:39

A function definition is also a declaration. That means when you have

int asdf(void);
int asdf(int a) {
    return a;
}

You have declared two version of asdf, one taking an int and one taking nothing. You then go on to use the int version when you use

std::cout << asdf(6) << std::endl; //-> 6

There is nothing wrong with that since the int version is defined.

What would be an issue would be if you try to use

asdf();

When you do that since it has be declared but not defined you should get an

undefined reference to `asdf()'

Since there is no definition. This has nothing to do with the most vexing parse but just the difference between function declarations and definitions.

查看更多
再贱就再见
3楼-- · 2019-09-15 18:46

Your proposal would not completely remove the ambiguity.

Consider:

#include <string>

struct Foo { ... };

int main(int argc, char **argv) {
    Foo bar(std::string(argv[1]));
}

This looks like it could be declaring a local variable bar of type Foo, passing a std::string to its constructor.

But what it actually means is:

Foo bar(std::string *argv);

I.e. declare bar as a function taking an argument of type "pointer to std::string" and returning a Foo.

This example can't be fixed by adding void.

查看更多
登录 后发表回答