Upcasting непрозрачный указатель
Я пытаюсь использовать прыщ идиома. В частности, класс реализации будет реализовывать другой интерфейс:
// 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 ... */
};
У меня вопрос, какие приведения могут быть использованы в следующей ситуации?
// 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 ... */
}
};
Будет ли dynamic_cast работать нормально в этом случае? Есть ли другие типы бросков, которые можно использовать в этом случае?
2 ответа
Насколько я могу судить, нет гарантированного способа сделать то, что вы хотите. Приведение reinterpret_cast или c-style может сработать (поведение не определено), но все остальные являются неопределенным поведением, когда это позволяет вообще его скомпилировать.
5.2.7.2 из n3242 (я знаю, что это не официальный стандарт, но он должен быть близок) говорит о dynamic_cast(v),
Если T является типом указателя, v должен быть prvalue указателя на завершенный тип класса, а результатом является prvalue типа T. Если T является lvalue ссылочным типом, v должен быть lvalue полного типа класса, и результатом является lvalue типа, на который ссылается T. Если T является ссылочным типом rvalue, v должно быть выражением, имеющим полный тип класса, а результатом является xvalue типа, на который ссылается T.
Так что dynamic_cast не работает.
static_cast не работает, так как между этими двумя типами не определено допустимого преобразования.
5.2.10.7 говорит о reinterpret_cast(v),
Указатель на объект может быть явно преобразован в указатель на другой тип объекта69. Когда значение v типа "указатель на T1" преобразуется в тип "указатель на cv T2", результатом является static_cast(static_cast(v)) если и T1, и T2 являются типами стандартной компоновки (3.9) и требования к выравниванию T2 не являются более строгими, чем требования к T1. Преобразование значения типа "указатель на T1" в тип "указатель на T2" (где T1 и T2 являются типами объектов, а требования к выравниванию для T2 не более строгие, чем требования для T1) и обратно к исходному типу, дает исходный значение указателя Результат любого другого такого преобразования указателя не определен.
поэтому reinterpret_cast может работать.
И, наконец, отказ от использования приведения не работает, потому что компилятор не знает об отношениях между типами.
Если я не ошибаюсь, вам вообще не нужно явное приведение, потому что SomeInterface
это базовый класс _PublicClass_impl
, и вы всегда можете неявно привести к базовому классу.
Я на самом деле пытался скомпилировать ваш код безdynamic_cast
в GCC (4.5.1), и действительно нет ошибок или предупреждений (я определил SomeInterface
как пустой класс).
Однако меня заинтересовал один связанный вопрос: почему компилятор учитывает тот факт, что SomeInterface
действительно базовый класс _PublicClass_impl
хотя последний является непрозрачным в данном вопросе?
Наиболее близким к объяснению этого я нашел §11.2, пункт 5 стандарта C++:
Если базовый класс доступен, можно неявно преобразовать указатель на производный класс в указатель на этот базовый класс (4.10, 4.11).
Если по какой-либо причине вы хотите использовать явное приведение, все же подойдет простое статическое приведение:
SomeInterface *interface = static_cast<SomeInterface *>(_impl);