简体   繁体   中英

Upcasting opaque pointer

I'm trying to use pimpl idiom. In particular, the implementation class would implement another interface:

// public_class.h
class PublicClass
{
public:
    /* public interfaces here */
private:
    class _PublicClass_impl;
    friend class _PublicClass_impl;
protected:
    _PublicClass_impl * const _impl;
};

// public_class.cpp
class PublicClass::_PublicClass_impl : public SomeInterface
{
    friend class PublicClass;
    /* all sort of stuff ... */
};

My question is, what casts can be used in the following situation?

// some_other_class.h
class SomeOtherClass : private PublicClass
{
    void some_function()
    {
        // definition of _PublicClass_impl is unknown
        // thus, _impl is opaque

        SomeInterface * interface = dynamic_cast<SomeInterface *>(_impl); //??
        /* more code ... */
     }
};

Would dynamic_cast work fine in this case? Are there any other types of cast that can be used in this case?

If I am not mistaken, you don't need any explicit cast at all, because SomeInterface is the base class of _PublicClass_impl , and you can always implicitly cast to a base class.

I actually tried compiling your code without the dynamic_cast in GCC (4.5.1), and indeed there is no error or warning (I defined SomeInterface as an empty class).

One related question intrigued me, though: Why does the compiler take into account the fact that SomeInterface is indeed the base class of _PublicClass_impl , although the latter is opaque at the point in question?

The closest I found to an explanation for this is §11.2, clause 5 of the C++ Standard:

If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class (4.10, 4.11).

If for some reason you want to use an explicit cast nevertheless, a simple static one should be fine:

SomeInterface *interface = static_cast<SomeInterface *>(_impl);

As far as I can tell, there isn't a gaurenteed way to do what you want. A reinterpret_cast or c-style cast might work (the behavior is unspecified), but the others are all undefined behavior when it lets you compile it at all.

5.2.7.2 of n3242 (I know it's not the official standard, but it should be close) says about dynamic_cast(v),

If T is a pointer type, v shall be a prvalue of a pointer to complete class type, and the result is a prvalue of type T. If T is an lvalue reference type, v shall be an lvalue of a complete class type, and the result is an lvalue of the type referred to by T. If T is an rvalue reference type, v shall be an expression having a complete class type, and the result is an xvalue of the type referred to by T.

So dynamic_cast doesn't work.

static_cast doesn't work since there aren't any valid conversion defined between the two types.

5.2.10.7 says about reinterpret_cast(v),

A pointer to an object can be explicitly converted to a pointer to a different object type.69 When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

so a reinterpret_cast might work.

And finally, not using a cast doesn't work because the compiler doesn't know about the relationship between the types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM