0

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?

touko
  • 410
  • 4
  • 7

2 Answers2

1

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);
jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • Hmm, so the compiler would understand that `_PublicClass_impl` extends `SomeInterface` even if the definition is not visible in that particular compilation block? – touko Apr 15 '12 at 03:57
  • It turns out it requires explicit cast. Error from my actual code: `sftp.cpp:26: error: cannot convert ‘sshfm::net::ssh::_ssh_impl* const’ to ‘sshfm::util::wrapper*’ in initialization`. `_ssh_impl` does implement the latter class, but it is unknown at compile time because its declaration is not visible. I chose to use an intermediate interface because I only need to access part of the encapsulated data. – touko Apr 15 '12 at 05:12
1

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.

Martin Shobe
  • 146
  • 2
  • I thought `reinterpret_cast` might work because RTTI would give the type information on runtime. – touko Apr 15 '12 at 04:03
  • The reason reinterpret_cast is likely to work is that what it's most likely to do when casting between unrelated pointer types is exactly the same thing it should be doing when converting from a derived class pointer to a base class pointer. Nothing. As long as you don't need to adjust the pointer when performing the cast, it should work. Unusual implementations might have different rules. – Martin Shobe Apr 15 '12 at 04:25