Search code examples
c++syntaxcastinginitializationlanguage-lawyer

Standard behavior for direct initialization of unsigned short


I noticed today that in the example code:

void print(unsigned short a) {
   std::cout << a << std::endl;
}

Initialization and use works like this:

print(short (5));

But not like this:

print(unsigned short(6));

main.cpp:16:8: error: expected primary-expression before 'unsigned' print(unsigned short(6));

And it's not to do with the type since this also works:

typedef unsigned short ushort;
print(ushort (6));

Live example.

So I went searching for what the standard says about value initialization. It turns out nothing:

The effects of value initialization are:

1) if T is a class type ...

2) if T is a non-union class type ...

2) if T is a class type ...

3) if T is an array type, ..

4) otherwise, the object is zero-initialized.

Modifications made for readability. Original source.

What are the rules about value initialization of POD types? What is the reason that unsigned qualified types can't be value initialized? Is this more to do with the fact they are rvalues?


Solution

  • What is the reason that unsigned qualified types can't be value initialized?

    It's just because only single-word type name could be used in functional cast expression, while unsigned short is not a single-word type name; short is.

    The functional cast expression consists of a simple type specifier or a typedef specifier (in other words, a single-word type name: unsigned int(expression) or int*(expression) are not valid), followed by a single expression in parentheses.

    As you showed, you can use typedef as the workaround, or add parentheses to change it to c-style cast expression, e.g. (unsigned short)(6), or (unsigned short)6.

    From the standard, §7.6.1.3/1 Explicit type conversion (functional notation) [expr.type.conv]:

    A simple-type-specifier or typename-specifier followed by a parenthesized optional expression-list or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer.

    And simple-type-specifier:

    simple-type-specifier:
      nested-name-specifier opt
     type-name
      nested-name-specifier template simple-template-id
      nested-name-specifier opt
     template-name
      char
      char16_t
      char32_t
      wchar_t
      bool
      short
      int
      long
      signed
      unsigned
      float
      double
      void
      auto
      decltype-specifier
    type-name:
      class-name
      enum-name
      typedef-name
      simple-template-id
    decltype-specifier:
      decltype ( expression )
      decltype ( auto )
    

    typename-specifier:

    typename-specifier:
      typename nested-name-specifier identifier
      typename nested-name-specifier template opt
     simple-template-id