Избавление от ошибки C2243

Можно ли избавиться от ошибки C2243?

class B {};
class D : protected B {};

D d;
B *p = &d;   // conversion from 'D *' to 'B &' exists, but is inaccessible

У меня была эта ошибка в моем приложении, и в конце концов мне удалось скомпилировать ее, выполнив явное преобразование:

D d;
B *p = (B*)&d;

Я не могу понять, почему создание класса D, унаследованного от B, делает неявное преобразование недоступным.

Я попытался избежать явного преобразования, создав оператор B() в классе D, чтобы сделать преобразование доступным:

class B {};
class D : protected B 
{
 public:
 operator B() {return *this;}
};

Но нет никакого способа.

Любое другое решение, позволяющее избежать явного преобразования?


person Lucas Ayala    schedule 24.09.2009    source источник


Ответы (6)


Если вы хотите разрешить преобразование, вы должны использовать открытое наследование.

Используя защищенное или закрытое наследование, вы заявляете, что тот факт, что производный тип наследуется от базового класса, является деталью, которая не должна быть видна извне: вот почему вы получаете эту ошибку.

Непубличное наследование следует рассматривать только как форму композиции с добавленной возможностью переопределения методов.

person Paolo Tedesco    schedule 24.09.2009
comment
Как насчет предоставления доступа только к константному интерфейсу базового класса? Итак, наследование в частном порядке и разрешение приведения к const base&? Спрашиваю, потому что не могу заставить его работать на MSVC... - person S. Paris; 07.04.2017
comment
Просто отвечая самому себе, вам нужно обрабатывать класс путем агрегации, а не наследования, иначе MSVC запутается и попытается решить преобразование с помощью частного наследования (скрывая оператор приведения) - person S. Paris; 07.04.2017

Поскольку protected и private наследование не являются отношениями is-a, они просто синтаксический сахар для композиции. Ваши классы могут быть переписаны точно так же, но вы теряете удобство, позволяющее компилятору определять b для вас и использовать члены b напрямую вместо явного обращения к ним:

class D
{
  protected:
  B b;
};

По второму пункту вашего вопроса:

operator B() {return *this;}

Эта строка связана с B и D. D* и B* полностью отличаются от B и D, хотя и являются указателями на них! чтобы указать указатели, вы можете переинтерпретировать указатель:

B *p = reinterpret_cast<B*>(&d); // TOTALLY WRONG, although it compiles :)

Не делайте вышеуказанную строку! Я думаю, вы могли бы дать нам больше информации о том, чего вы пытаетесь достичь.

person AraK    schedule 24.09.2009

Поскольку из дочерних элементов D и Ds никто не знает, что они являются родителем-потомком, поэтому вам нужно сделать это явно.

Вот что означает защищенное наследование, только ваша семья (дети) будет знать, что вы наследуете. И вы могли бы использовать его, например, в дочернем методе, там неявное преобразование было бы законным.

Если вы хотите иметь неявное преобразование ваших детей, вам нужно сделать это общедоступным, чтобы все знали.

person Arkaitz Jimenez    schedule 24.09.2009

Проблема здесь в том, что вы пытаетесь скрыть информацию, которую предоставляет атрибут protected. Если вы хотите получить доступ к экземпляру D как к B, почему вы унаследовали его как защищенный, а не общедоступный? Что вы говорите, используя защищенное наследование, так это то, что вы хотите, чтобы только экземпляры D и его потомки знали о компоненте B. Вам нужно еще раз взглянуть на то, чего вы хотите достичь.

Приведение в старом стиле C, которое вы используете, не имеет ни одной из тонкостей более новых приведения C++, поэтому оно дает вам код, который будет компилироваться, но проблема действительно заключается в наследовании.

person Tim Allman    schedule 24.09.2009

Вы пытались сделать оператор B() общедоступным в классе D? В коде, который вы показали, он будет помечен как защищенный и по-прежнему недоступный. Но я бы вообще избегал операторов преобразования, если это возможно.

Тем не менее, наследование защищенного B означает, что вы намерены предотвратить выполнение B* p = &d. Представьте, если бы B была на самом деле защищенной переменной-членом в верхней части D. Точно так же, как вы не можете получить доступ к D.b в этом случае, вы не можете получить доступ к d как к B*, если только вы не отбросите ее.

Так что либо публично наследуйте B, либо используйте свои приведения. Я бы пошел с публичным наследованием B, потому что наследование его защищенного в основном говорит: «Не используйте меня как B», что вы все равно пытаетесь сделать.

person AshleysBrain    schedule 24.09.2009

Поскольку никто извне не знает, что они являются родителем-потомком, вы можете выполнить это действие только в производном классе D. Это пример (тест в Visual Studio 2013):

class BASE{};

class BASE1 :protected BASE{};

class BASE2 :protected BASE1
{
public:
    void func(BASE &a, BASE1 &b){a = b;}
};

void main()
{
    BASE a;
    BASE1 b;
    BASE2 c;

    c.func(a, b);;
    system("pause");
 }
person Jean Leo    schedule 24.11.2014