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?
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.
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.
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.
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)'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With