As int()
and int{}
are constant expressions of value equal to 0
, I thought they are equivalent and interchangeable, thus compilers must treat them equally. For example,
int a[0]; //error: zero-sized array not allowed in ISO C++
int b[int()]; //error: zero-sized array not allowed in ISO C++
int c[int{}]; //error: zero-sized array not allowed in ISO C++
But it seems there are some corner cases where they're not interchangeable.
When initializing a pointer:
int *p = 0; //ok
int *q = int(); //error - by clang only
int *r = int{}; //error - by gcc and clang both
See GCC and Clang messages. I suspect this is a bug in both compilers, as I expect them to be interchangeable in this context, but I would be glad to be proven wrong. :-)
When passing to class template:
template<int N> struct X{};
X<0> x1; //ok
X<int{}> x2; //ok (same as X<0>)
X<int()> x3; //error
See GCC and Clang messages.
I find the syntax X<int()>
quite familiar as I've seen (and probably used) the similar syntax before, such as in std::function<int()>
, the template argument int()
is expected to be function type (instead of 0
) taking no argument and returning int
. But I want to know the section of the spec which says in this context int()
is to be treated as function type and is not equivalent to int{}
which is always 0
.
The expressions int()
and int{}
are both constant expression prvalues of integer type that evaluate to zero, and are therefore interchangeable with the literal 0
in any context that requires an integral constant expression prvalue of integer type which evaluates to zero.
Both expressions satisfy the requirements for a constant expression as specified in 5.19 Constant Expressions [expr.const].
Regarding X<int()>
, the standard specifies that int()
is not interpreted as an expression in this context:
14.3 Template arguments [temp.arg]
In a template-argument, an ambiguity between a type-id and an expression is resolved to a type-id, regardless of the form of the corresponding template-parameter.
Regarding pointer conversions:
4.10 Pointer conversions [conv.ptr]
A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type
std::nullptr_t
.
Based on the above paragraph, both int()
and int{}
are null pointer constant expressions. This points to a (very minor) compiler bug, although there is an open Defect Report (903) which may lead to this paragraph changing:
There was a strong consensus among the CWG that only the literal 0 should be considered a null pointer constant, not any arbitrary zero-valued constant expression as is currently specified.
The following wording deals with the value of the expression int()
:
8.5 Initializers [dcl.init]
To zero-initialize an object or reference of type T means:
[omitted clauses which don't apply]
— if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T
[...]
To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
[omitted clauses which don't apply]
— otherwise, the object is zero-initialized.
An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
And for the value of int{}
:
8.5.4 List-initialization [dcl.init.list]
List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
[omitted clauses which don't apply]
— Otherwise, if the initializer list has no elements, the object is value-initialized.
All quotes from C++ Working Draft Standard N3337.
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