简体   繁体   中英

C/C++: Calling function with no arguments with function which returns nothing

Why isn't it possible to call a function which takes no arguments with a function call as argument which does not return any value (which IMHO is equivalent to calling a function which takes no arguments with no arguments).

For example:

void foo(void) {...}
void bar(void) {...}

foo(bar())

Don't get me wrong, I know void is not a value and that it cannot be treated like one.

With my logic it would make sense and it should be possible to do that. I mean, why not? Any argument why that should not be possible?

I'm not convinced that any of the reasons I've heard are good ones.

See, in C++, you can return a void function's result:

void foo() {
    // ...
}

void bar() {
    // ...
    return foo();
}

Yes, it's exactly the same as:

foo();
return;

but is much more consistent with generic programming, so that you can make a forwarding function work without having to worry about whether the function being forwarded has void return.

So, if a similar system applied so that a void return constituted a nullary call in a function composition scenario, that could make function composition more generic too.

Because, according to paragraph 3.9.1/9 of the standard,

An expression of type void shall be used only as an expression statement (6.2), as an operand of a comma expression (5.18), as a second or third operand of ?: (5.16), as the operand of typeid, or as the expression in a return statement (6.6.3) for a function with the return type void.

C/C++ just isn't designed to be that generic. You do get return returns_void(); for tail-call optimization, that's functional-ish, right? :vP

Edit: The above rule would still allow you to call takes_void(3) with 3 converted to void . This is forbidden by 8.3.5/2:

If the parameter-declaration-clause is empty, the function takes no arguments. The parameter list (void) is equivalent to the empty parameter list. Except for this spe- cial case, void shall not be a parameter type (though types derived from void, such as void*, can).

If the (void) parameter list was treated uniformly with all other parameter lists in C/C++, the semantic meaning if such parameter declaration would be "a single parameter of type void ". If that was the case, it is quite possible that for the purposes of uniformity the language would allow to "chain" the calls to such functions as shown in your example. (At least C++ probably would, since it allows this kind of uniformity in return statements).

However, in C++ language as well as in C, parameter list that has the form (void) is not treated uniformly with other forms of parameter lists. Instead, it has a special meaning. It means that the function has no parameters at all . (Equivalent to empty () in C++).

In other words, the function declared with (void) parameter list takes zero parameters. You are supplying one . This is what makes it illegal. Considering the special meaning of (void) parameter list in C/C++, allowing

foo(bar());

would not be much different from allowing

foo(bar(), bar());
foo(bar(), bar(), bar());

as well. Qualitatively, 2 and 3 are as much different from 0, as 1 is.

Don't be misled by the notation. Rather annoyingly, C uses the f(void) prototype to mean " f expects no parameters" rather than " f expects a single parameter of type void ". The notation f() was kept to mean "the number of parameters that f expects is not known, so you can call it with any number of parameters you like, and good luck to you". Before ANSI Standard C (aka C89), there was no such thing as a function prototype, and you needed a tool like lint to protect you against even the most mundane sorts of errors with parameters, such as passing the wrong number of parameters or passing a parameter with a wildly incompatible type.

As to why you can't use the value of a function return void as a value, or why you can't pass a parameter to a function that expects no parameters, those restrictions are in place to protect you from making the most mundane sorts of errors with parameters.

it does make kind of sense ( bar produces nothing, foo consumes nothing, therefore foo(bar()) should be allowed). OTOH, it would only be good to confuse the reader. if you want to be l33t, there's always , , && and || operators to emulate semicolons.

因为void不允许您指定参数名称,这是为了捕获第一个函数的返回值所必需的,无论它实际返回什么。

Let's pretend it works. Can you think of a single hypothetical case where

bar();
foo();

which does precisely the same thing isn't vastly more readable and sensible?

Edited. Yes, yes. :)

At the moment i have something like this in my code: #define EVAL(f) f(++evalcounter) Dont ask me why i use it, i just have to. Normally f does not take any values. I just added this (global) integer just to count something. Wouldnt it be much better if i had something like #define EVAL(f) f( dummy(++evalcounter) ) ?

Have you tried the comma operator:

#define EVAL(f)   (++evalcounter, f())

I'll add one extra declaration: void baz(int);

Now obviously if an argument with type void is equal to no arguments, then two arguments, one of which has type void would be equal to one argument (as 0 = 1*x <=> 1 = 1+1*x)

So, obviously this is legal: baz(1, bar()); . But also (since void is "nothing") baz(bar(),1); .

And we can continue in this sense: baz(bar(), 1, bar()); . After all, "void is nothing".

Either you ban it outright, put in arbitrary restrictions, or you end up allowing ridiculous constructs. I agree with the choice for the first resolution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM