Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
205 views
in Technique[技术] by (71.8m points)

c++ - Are there cases where downcasting an actual Base to a Derived would be defined?

In the general case, it is (a very well deserved) Undefined Behavior to dowcast from a (dynamic) Base to one of the deriving classes Derived

The obvious UB

class Base
{
public:
    virtual void foo()
    { /* does something */?}

    int a;
}

class Derived : public Base
{
public:
    virtual void foo()
    { /* does something different */?}

    double b;
}

Base obj;
Derived derObj = *static_cast<Derived *>(&obj);  // <- here come the demons

In the current implementation approach of compilers, here there would obviously be at least the problems of inconsistent values in the Vtable and b containing garbage values. So it makes sense the standard does not define the behavior of a downcast in those conditions.

The not so obvious naive case

Yet I was curious to know if there were some concessions to this rule in specific cases ? For an example :

class Base
{
public:
    void foo()
    { /* does something */?}

    int a = 1;
    double b = 2.;
}

class DerivedForInt : public Base
{
    int getVal()
    { return a }
}

Base obj;
DerivedForInt derObj = *static_cast<DerivedForInt *>(&obj);  // <- still an UB ?

Here we can easily imagine compiler doing the right thing. But from the standard perspective, is it still undefined ?

Edit : static_cast is a random choice for illustration purpose, it is also interesting if working with other casts !

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Ok, I'll probably get shred into pieces for this answer...

Obviously, as the other answers stated this is undefined behaviour, as found in the standard. But if your Base class has standard layout and your DerivedForInt class does not add new data members it will have the same (standard) layout.

Under these conditions your cast should cause no troubles even it being UB. According to one of the sources, it is at least safe to do a

DerivedForInt *derived = reinterpret_cast<DerivedForInt*>(&base.a);

Sources:

What are Aggregates and PODs and how/why are they special?

PODs and inheritance in C++11. Does the address of the struct == address of the first member?

From the second link:

Here's the definition, from the standard section 9 [class]:

A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes,
  • either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.

And the property you want is then guaranteed (section 9.2 [class.mem]):

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

This is actually better than the old requirement, because the ability to reinterpret_cast isn't lost by adding non-trivial constructors and/or destructor.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...