Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function call missing argument list warning

Tags:

c++

c++11

I'm reading over "The C++ Programming Language - Fourth Edition" and I was typing up a simple exercise just to get a hang of C++ syntax and I accidentally stumbled across something that made me raise an eyebrow. In short, I forgot to add () on accept in main:

bool accept()
{
    cout << "Do you want to proceed (y or n)?\n";

    char answer = 0;
    cin >> answer;

    if (answer == 'y')
    {
        return true;
    }
    return false;

}

int main()
{
    accept;
}

This runs and compiles and produces (in VS2015) a

C4551 - function call missing argument list

I've found myself reading lambdas and a bunch of questions on SO that should be be closed because they are mostly asking to "debug my code."

I figured that if the code compiles and runs, and the function contains a blocking statement (waiting for user input) and a return type, that all the code would get executed as expected regardless of missing the parentheses; that is not the case though.

Additionally, I figured I would change the call to accept in main, to bool a = accept; cout << a; to try and prevent any optimization (if that was what was actually happening), and that did not call the accept() code either.

What I'm curious to know is:

  1. What is the call to accept getting compiled into?
  2. Why isn't the code in accept getting called
  3. Why is this only a warning, and not an error (I know I can change configuration to display it as error, I'm more so questioning how this is accepted syntax by default when actual result differs from expected result so "dramatically?" This question might be opinion-based, omit it if you agree it is.)
  4. Running the bool a = accept; cout << a; code in main produces 1 as the output. How can this be when false is the default bool value (in C# at least) and there is nothing to return a true value since the accept code does not get executed?
like image 365
Kcvin Avatar asked Dec 28 '15 18:12

Kcvin


2 Answers

  1. There is no "call to accept". See #3.

  2. Because of #1.

  3. The use of a function name without function call syntax (ie: ()) means that you're accessing the function itself. You could, for example, store it in a function pointer (through function-to-pointer decaying):

    using my_func = bool(*)(); //Function that takes nothing and returns a bool.
    my_func var = accept; //Store a pointer to `accept`.
    

    You could then issue var();, which would call accept.

    However, because you never store the function, the compiler guesses that you probably meant to call the function, not to access the function's pointer. accept; however is a legal C++ statement, therefore the compiler cannot error on it. It can emit a warning, since the statement accomplishes nothing and you probably meant to call the function. It's no different from a statement like 1;: perfectly legal, but utterly useless.

  4. It does this because of C++ trickery. Non-null pointers decay to the boolean value true. And accept decays to a function pointer that is not null. Therefore, when converted to a bool, it will be true. You're still not calling the function.

like image 132
Nicol Bolas Avatar answered Oct 24 '22 09:10

Nicol Bolas


In C++, any expression followed by a semicolon is a legal statement. (Why? Because C let you do this, I think). That means that all of the following are legal statements:

 5;
 3 + 5;
 1 % 2 == 0;

The effect of a statement of this form is that the expression is evaluated and then discarded. A good optimizing compiler would just eliminate all of the logic here, since none of these have any side-effects.

In your case, writing

accept;

is a legal statement because accept is an expression evaluating to a reference to the function accept. That means that accept; as a statement means "evaluate the address of accept, then discard it." The reason that no function is called here is that a function name by itself doesn't invoke the function; you need the parentheses (the function call operator) to actually make the call. This is useful, for example, if you want to pass a function into another function. For example, you might want to pass a comparison function into std::sort, like this:

std::sort(range.begin(), range.end(), nameOfMyComparisonFunction)

Here, it would be a real problem if this tried calling nameOfMyComparisonFunction, since the arguments can't be known until the sorting routine starts off.

So why is this a warning and not an error? Well, it's perfectly legal C++ code, so the compiler can't compliantly call it an error. However, the compiler is right to flag it as a warning, since it almost certainly means you made an error. That said, most compilers have some setting that reports warnings as errors, and if you crank up the warning level high enough the compiler probably would say "this is so suspicious that I'm going to assume you messed something up."

As to your last one - why does

bool a = accept;

end up setting a to true? In C++, any non-null pointer implicitly converts to true and any null pointer implicitly converts to false. In C++, functions are implicitly convertible to pointers to themselves, so in this case accept evaluates to the address of the accept function, which is non-null, so it sets a to true. When you then write

cout << a << endl;

the value is printed as 1, because bool values are printed as 1 and 0 rather than true and false by default. That said, you could write

cout << boolalpha << a << endl;

and you'll see true printed instead.

Hope this helps!

like image 32
templatetypedef Avatar answered Oct 24 '22 09:10

templatetypedef