2

Does the C++ standard define a particular behaviour if you make an old C-style cast from type A to type B where type A cannot be cast to type B and vice versa?

Would there be a known visible behavior that can be assumed to be symptom of a illegal cast in runtime using this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Allahjane
  • 1,920
  • 3
  • 24
  • 42
  • I think you should provide an example. E.g.: are you talking about POD types, classes, primitive types, pointers, ...? – UnholySheep Dec 31 '16 at 22:04
  • 1
    A C-style cast can be equivalent to any of 3 different C++ casts ( `static_cast`, `reinterpret_cast` or `const_cast`) depending on the actual code. Of these `static_cast` would throw a compile time error, while `reinterpret_cast` would (in most cases) lead to UB (if the cast was illegal) – UnholySheep Dec 31 '16 at 22:12
  • You'd probably find that you get 'undefined behaviour', and there are no standard symptoms for 'undefined behaviour' — the program doesn't behave as you expected or intended, but that's about all you can say. And it might even behave as you expected with some compiler(s); it isn't obliged to go wrong with any of them, though it could. – Jonathan Leffler Dec 31 '16 at 22:13
  • If there is no equivalent c++ cast from A to B, then the c style cast will fail. – MikeMB Jan 01 '17 at 01:16
  • @MikeMB by fail you mean compile time error or undefined runtime behavior? Because as other answers state you can actually do that cast in c style where the equivalent cast is illegal. – Allahjane Jan 01 '17 at 05:34
  • @Allahjane: I meant compile-time error. C-style casts are defined in terms of c++ casts. And while you can cast any pointer type into any other pointer type (the cast is allowed, only dereferencing might be UB) the compiler won't allow you to cast an arbitrary variable of type A to type B if there isn't a relationship between them. – MikeMB Jan 01 '17 at 11:53

2 Answers2

4

Only one of the four C++-style casts determines the validity of the cast at runtime, namely dynamic_cast.

A C-style cast corresponds to a combination of the other three casts (static_cast, reinterpret_cast, const_cast). The validity of those casts is determined at compile-time, or if it cannot be determined at compile-time then the cast is assumed to be valid. A C-style cast never acts like dynamic_cast.

So a C-style cast that "fails" at runtime, i.e. breaks the validity assumption, causes undefined behavior. So:

Does the C++ standard define a particular behaviour if you make an old C-style cast from type A to type B where type A cannot be cast to type B and vice versa?

No.

Would there be a known visible behavior that can be assumed to be symptom of a illegal cast in runtime using this?

No.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • [Also see here](http://stackoverflow.com/questions/17925124/can-i-cast-a-derived-class-to-a-private-base-class-using-c-style-cast), this C-style cast is not equivalent to any combination of C++-style casts – M.M Dec 31 '16 at 22:37
0

Compiler will catch some of them, but not all. Here is an example of a disastrous cast:

#include <iostream>
#include <string>

using namespace std;

class A {
public:
        A(std::string s, int x) : m_s(s), m_x(x) {} 

        void print() { cout << "A::print(): str = " << m_s << ", x = " << m_x << endl; }

private:  
        std::string m_s;
        int m_x;
};

class B {
public:
        B(int x) : m_x(x) {}
        void print() { m_x++; cout << "B::print(): x = " << m_x << endl; }

private:
        int m_x;

};

int main(int argc, char **argv) {

        A *a = new A("abc", 1);
        a->print();
        B *b = (B*)a;
        b->print();

        a->print();

        return 0;
}

Result on gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609:

A::print(): str = abc, x = 1
B::print(): x = 24828977
A::print(): str = bc, x = 1

Yes, we called B's methods on A's data, which means that our C-style cast worked as reinterpret_cast. reinterpret_cast just takes a memory region and allows you to treat is as something different. No seatbelts here.

On the other hand, if you try to compile something like this

    A a;
    B b;
    a = (A)b;
    return 0;

it will result in static_cast and compile-time error. So C-style casting result depends on context.

This is NIGHTMARE when dealing with templates and auto.

To avoid this problem, use static_cast (for compile-time checks) and dynamic_cast (for runtime checks) or reinterpret_cast (for pointers and POD), clearly stating your intent.

ezaquarii
  • 1,914
  • 13
  • 15
  • Actually your results may only occur on your machine/compiler - I get a [runtime error](http://ideone.com/CsdaXh). The line `b->print();` is invoking *undefined behavior* – UnholySheep Dec 31 '16 at 22:26
  • B::print() modifies `sizeof(int)` bytes occupied by `std::string`. You may observe memory corruption there. – ezaquarii Dec 31 '16 at 22:32
  • The [documentation of `reinterpret_cast`](http://en.cppreference.com/w/cpp/language/reinterpret_cast) is clear on this - you are invoking UB. Also not sure how memory corruption would matter for the fact that your results are not guaranteed and a different compiler throws a runtime error. – UnholySheep Dec 31 '16 at 22:40