0

I discover this compiler trick and I cannot find a name for. Have you any idea?
On an Intel processor, I can cast a variable from its base class to an inherited class. It works with MSVC, gcc and clang and I am very confused.

#include <string>
#include <iostream>

class A
{
public:
    virtual std::string print() const { return "A"; }
};

class B : public A
{
public:
    std::string print() const override { return "B"; }
    std::string printOther() const { return "other"; }
};

int main()
{
    A a;

    std::cout << a.print() << std::endl; // print A

    std::cout << static_cast<const B&>(a).print() << std::endl; // print A
    std::cout << static_cast<const B&>(a).B::print() << std::endl; // print B
    std::cout << static_cast<const B&>(a).printOther() << std::endl; // print other

    try
    {
        std::cout << dynamic_cast<const B&>(a).printOther() << std::endl; // throw std::bad_cast
    }
    catch (const std::bad_cast& e)
    {
        std::cout << e.what() << std::endl; // print std::bad_cast
    }

    std::cout << ((const B&)a).print() << std::endl; // print A
    std::cout << ((const B&)a).B::print() << std::endl; // print B
    std::cout << ((const B&)a).printOther() << std::endl; // print other

    std::cout << reinterpret_cast<const B&>(a).print() << std::endl; // print A
    std::cout << reinterpret_cast<const B&>(a).B::print() << std::endl; // print B
    std::cout << reinterpret_cast<const B&>(a).printOther() << std::endl; // print other

    // error: invalid 'const_cast' from type 'A*' to type 'const B*'
    //std::cout << const_cast<const B&>(a).print() << std::endl; // print A
    //std::cout << const_cast<const B&>(a).printOther() << std::endl; // print other

    return 0;
}
5
  • 1
    The most common result of UB is the appearance of working correctly. Commented Jun 8, 2022 at 10:54
  • Just because a C++ compiler compiles a program without errors does not have any guarantees that it's bug free or that it will work 100% correctly. As you continue learning C++ you will encounter this situation many, many times. Commented Jun 8, 2022 at 10:54
  • 1
    Add some data members to your classes and you will probably see that the phoney casts don't actually work, at all. And the name for this "trick" is, as others have indicated, "Undefined Behaviour". Commented Jun 8, 2022 at 10:57
  • Your confusion is caused by the expectation that incorrect programs will behave badly. Drop that expectation and you will understand, and also you will realise that this makes programming C++ a bit harder than you thought. Commented Jun 8, 2022 at 11:02
  • @AdrianMole I understand, the members of my inherited class are not allocated! Commented Jun 8, 2022 at 11:03

1 Answer 1

3

Yes, static_cast can cast a class to a reference to a derived class. That is not a trick or compiler-specific. That is one of the specified purposes of static_cast in the C++ standard.

However, this cast has undefined behavior if the object isn't actually a base subobject of a derived class object.

In your case here you never created a B object, only a A object. Therefore all of the static_cast have undefined behavior and your program is broken.


(const B&)a effectively is defined to do the same as static_cast<const B&>(a) if the type B is a derived class type of the type of a. So all of them in your code also have undefined behavior and are broken.


dynamic_cast<const B&>(a) is allowed here since your class is polymorphic (has a virtual member function). It will work correctly and fail with an exception if a isn't actually a base subobject of a B object rather than causing undefined behavior.

dynamic_cast is the only cast that you may use if you are not sure that a actually is a base subobject of a B.


reinterpret_cast<const B&>(a) does something completely different than the above and also causes undefined behavior, because your classes are not standard layout and therefore definitively not pointer-interconvertible.

Sign up to request clarification or add additional context in comments.

4 Comments

My first assertion is to think my program is broken. But on Compiler Explorer, every thing is well defined.
@Theo A program happening to give the output you expect doesn't mean that it doesn't have undefined behavior and isn't therefore broken.
@Theo Add some data members that are initialized in the constructors. You will see that the phoney B object isn't getting its data members initialized.
Thanks you, I understand what I can do or not!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.