質問

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top