Does specifying the use of void in the declaration of a function that takes no arguments address The Most Vexing Parse?
Asked Answered
H

2

0

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++

Hartwell answered 17/1, 2017 at 18:37 Comment(8)
"Should the above not generate a warning, if not throw an error?" No - why would you think it should? That code declares two functions, provides a definition for one of them, and calls that one.Bifoliolate
The most vexing parse is how do you separate foo bar(); from a function named bar returning a foo to a variable of type foo named bar that is default constructed.Marinmarina
@latedeveloper 2, Because of the argument mismatch between the function declaration and definition; specifically, the declaration specifies void arguments, while the definition specifies an integer argument.Hartwell
@Hartwell The declaration and definition refer to two different functions.Petite
@kmiklas: Are you asking if we could fix the most-vexing-parse by breaking tons of existing C++ code? If so, the answer is "sure we can".Errolerroll
@Hartwell it is not as definition of the same function as the declaration, you declare two separate functions, one taking no arguments, and one taking one argument. A key difference from C.Vania
The C++ committee chose the solution of having foo bar{}; being a variable, and keep foo bar(); as the function declaration it has always been.Lunar
@Marinmarina - foo bar(); is not the most vexing parse (see the link in the question). It's a function declaration, nothing more. People often confuse it with object creation, but that's not a parsing issue. The most vexing parse is far more vexing.Milksop
M
2

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.

Marinmarina answered 17/1, 2017 at 18:49 Comment(3)
Ah, because the argument list is different, the function signature is different, and the call asdf(6) will match int asdf(int a) {, correct?Hartwell
@Hartwell Yes. You have overloaded the function. You just have not provided a definition for the void overload.Marinmarina
Thx. I got tangled up this business of rooting out a solution to The Most Vexing Parse, and missed the forest through the trees.Hartwell
P
2

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.

Petite answered 17/1, 2017 at 18:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.