10

While I have been reading about datatype conversion, I saw this example:

void intval()
{
    for (char c; cin >> c; )
    cout << "the value of '" << c << "' is " << int{c} << '\n';
}

I know that we can cast using:

  1. int(c)
  2. (int) c
  3. static_cast<int>(c)

My questions:

Q1: Is int{c} another way of casting data types?

Q2: After some research on the net, I know that C++ casting is different and it have the compiler check the casting possibility at the compile time, but what are the differences between 1 and 2? And how int{c} is different if it is just another casting way?

Q3: Are there any other ways to explicitly convert/cast?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Shadi
  • 1,701
  • 2
  • 14
  • 27
  • 3
    I thought that the curly braces were to do with list initialization, see this answer http://stackoverflow.com/a/18222927/3110529 TL;DR: List initialization does not allow narrowing (§iso.8.5.4) meaning that it will catch unsafe type conversions. – Dillanm Mar 23 '17 at 12:28
  • In particular, if `d` is a `double` and `double` is the same size as `uint64_t`, `static_cast(d)` and `reinterpret_cast(d)` convert in completely different ways. C-style casts don't have that flexibility; you would need to type-pun through a union or pointer to reinterpret the bits. – Davislor Mar 26 '17 at 20:16
  • @Davislor Since when is `reinterpret_cast` a thing? – curiousguy Jun 06 '19 at 17:33
  • @curiousguy You’re correct: the proper syntax would be to `reinterpret_cast` to a *reference*, `uint64_t&`. This has been legal (but unspecified) since, I believe, C++03. – Davislor Jun 06 '19 at 17:55

3 Answers3

11

Is int{c} another way of casting data types?

Yes. T{value} creates a temporary of type T that is direct-list-initialized with the specified braced-init-list. This cast does have an advantage over T(value) in that T{value} can be used to create a temporary array. That would be done like

int main() {
    using int_array = int[5];
    for( auto e : int_array{1,2,3,4,5})
        std::cout << e;
}

It also comes with the caveat that a narrowing conversion is a error

int main() {
    int(10000000000ll);  // warning only, still compiles
    int{10000000000ll};  // hard error mandated by the standard
}

After some research on the net, I know that C++ casting is different and it have the compiler check the casting possibility at the compile time, but what are the differences between 1 and 2?

The big difference between T(value) and (T)value is that in T(value), T must be a single word. For example

int main() {
    unsigned int(10000000); // error
    (unsigned int)10000000; // compiles
}

Q3: Are there any other ways to explicitly convert/cast?

Well in C++ they want you to use the C++ casts which are static_cast, reinterpret_cast, dynamic_cast, and const_cast. Those are preferred over c style cast as a c style cast will do all of those where the C++ versions have certain limitations and come with certain guarantees.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
7

int(c) is the the C++ version of the C-style cast (int)c. It first attempts a const_cast<int>(c), then (failing that) a static_cast<int>(c) followed by reinterpret_cast.

int{c} is a slightly different kettle of fish. Strictly, this is list initialization and has more stringent rules. In particular, narrowing conversions are not allowed, i.e.

int x;
char s{x};  // error

Therefore, it is recommended to use this (rather than casts) unless you know that narrowing conversions are acceptable.

For other than builtin types, there is, in addition to the casts mentioned above, also dynamic_cast.

Emerick Rogul
  • 6,706
  • 3
  • 32
  • 39
Walter
  • 44,150
  • 20
  • 113
  • 196
3

Q1: Yes. It is almost the same as a functional style cast (int(c)), and works because of c++11's uniform initialization. However brace initialization does have a few caveats, for example narrowing conversions (like long l = 5; char c{l};) will generate a warning.

Q2: 1 and 2 are equivalent, although there are some situations where one works and not the other.

// long long(c); // Breaks unless you make a typedef for 'long long'
(long long)c;    // Works fine

template <class In, class Out>
Out convert(const In& in) {
    // return (Out)in; // Only works if 'In' is a primitive type
    return Out(in);    // Works regardless of the type of 'In' (assuming an appropriate constructor exists)
}

Q3: The only example of C++-style casting that you mention is static_cast. There are other C++ casts as well:

  • dynamic_cast
  • reinterpret_cast
  • const_cast
0x5453
  • 12,753
  • 1
  • 32
  • 61
  • " (like long l = 5; char c{l};) will generate a warning." I think it should generate a compile error. – Catskul Jun 08 '22 at 17:36