Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ compiler does not warn about missing argument before end of default arguments

I declared a C++ function declaration with 3 arguments, two of which had defaults like so.

void func(int const n, bool const flag=true, int *array=NULL) {
  /* print contents of array */
}

When I invoked the function erroneously, omitting the second argument but including the third argument, like so

int array[5]={1,2,3,4,5};
func(5,array);

neither gcc nor intel compilers (default ones on Ubuntu 14.04 LTS) complained that the last argument was specified without specifying the second last one. The code ran but sent in NULL for array (I expected the code to fail).

My question is why didn't the compilers complain that it could not find a matching function since the signature of my invocation should have appeared as

funct(int const, int *)

What options can I turn on during compiling to trigger a warning about this erroneous usage?

like image 441
raovgarimella Avatar asked Mar 04 '15 18:03

raovgarimella


4 Answers

The compiler decays the array to a pointer and converts the pointer to a bool and proceeds.

Update

From the C++11 Standard:

4 Standard conversions [conv]

1 Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions. A standard conversion sequence is a sequence of standard conversions in the following order:

— Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.

— Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.

— Zero or one qualification conversion.

[ Note: A standard conversion sequence can be empty, i.e., it can consist of no conversions. —end note ]

A standard conversion sequence will be applied to an expression if necessary to convert it to a required destination type.

like image 141
R Sahu Avatar answered Nov 15 '22 06:11

R Sahu


According to the C++ Standard (4 Standard conversions)

1 Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions. A standard conversion sequence is a sequence of standard conversions in the following order:

— Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.

— Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.

— Zero or one qualification conversion.

And

4.12 Boolean conversions

1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (8.5), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

So this function call

func(5,array);

is equivalent to

func(5,array, NULL);

the second argument at first was converted from array to pointer (array-to-pointer conversion) and then was converted to boolean true (boolean conversions).

Thus this call is a valid call of the function. The compiler implicitly converts arguments to appropriate types.

like image 44
Vlad from Moscow Avatar answered Nov 15 '22 04:11

Vlad from Moscow


There is an implicit casting to bool taking place. With Visual C++ you would get a C4800 warning. In gcc you can request specific warnings with options beginning with ‘-W’, for example -Wimplicit to request warnings on implicit declarations. See Options to Request or Suppress Warnings for the full documentation.

like image 33
Satara Avatar answered Nov 15 '22 04:11

Satara


In this particular case solution could be using different argument order:

void func(int const n, int *array=NULL, bool const flag=true ) {
  /* print contents of array */
}

If you ommit second parameter boolean value cannot be implicitly converted to a pointer (unless you have bad habbit of using 0 as false), so it will fail to compile (or at least provide warning).

void func(int const n, int *array = nullptr, bool const flag=true ) {
      /* print contents of array */
}

int main()
{
    func( 0, false );
}

warning: converting 'false' to pointer type for argument 2 of 'void func(int, int*, bool)' [-Wconversion-null]

But in general it shows that you should use different technique. For example using std::vector instead of raw pointer.

typedef std::vector<int> int_vec;
void func( bool const flag=true, const int_vec &array = int_vec() ) {
      /* print contents of array */
}

int main()
{
    int_vec array {1,2,3,4,5};
    func( array );
}

error: cannot convert 'int_vec {aka std::vector}' to 'bool' for argument '1' to 'void func( bool, int_vec)'

like image 29
Slava Avatar answered Nov 15 '22 06:11

Slava