как реализовать приведение к частному базовому классу в С++

Как реализовать приведение к частному базовому классу в C++? Я не хочу использовать хаки, такие как добавление друга и т. д. Определение оператора публичного кастинга не работает.

ИЗМЕНИТЬ:

Например, у меня есть:

class A {
//base class
}

class AX : private A {
//a child
}

class AY : private A {
//another specialized child
}

class B {
//base class
void do (A a) {//do
    }
}

class BX : private B {
//a child
void do (AX a) {
     B::do( static_cast <A> (a) );
    }
}

class BY : private B {
//another specialized child
void do (AY a) {
    B::do( static_cast <A> (a) );
    }
}

ИЗМЕНИТЬ2

Почему я это делаю?

Предположим, мне нужно определить какое-то свойство, которое довольно тяжеловесно и может иметь несколько схожих типов (например, VelocityX, VelocityY и т. д.). Затем я хочу иметь классы, которые могут иметь любой набор этих свойств. Если я хочу обработать эти свойства, очевидно, что я скорее приведу их к их базовому типу, чем добавлю реализацию для каждого варианта. Я не использую публичное наследование, потому что лучше явно приводить типы там, где это необходимо, чем неявно отображать частный интерфейс. Не проблема, но хотелось бы решения :)


person Community    schedule 17.09.2010    source источник
comment
Не могли бы вы рассказать нам, что вы хотите сделать? Это выглядит как очень плохой дизайн. Почему бы вам не использовать публичное наследование? Или композиция и геттерная функция? Не могли бы вы также предоставить нам код, который не работает?   -  person Alexandre C.    schedule 17.09.2010
comment
Обратите внимание, что многие не считают friend взломом.   -  person Martin Ba    schedule 18.09.2010
comment
Когда у вас есть много классов, которые, возможно, нужно добавить в этот список друзей, я считаю это хаком.   -  person    schedule 18.09.2010
comment
Хорошенькая у тебя нарезка...   -  person gpeche    schedule 18.09.2010
comment
private наследование значит Не рассматривайте это как основу. Это имеет очень сильный запах кода. Я бы предложил полностью переосмыслить ваш подход.   -  person greyfade    schedule 18.09.2010


Ответы (3)


Если определение публичного оператора приведения не работает, вы можете попробовать использовать обычную функцию:

class D: private B {
    public:
        B& asB() { return static_cast<B&>(*this); }
};
...
D d;
d.asB().methodInB();
...

В любом случае, в чем смысл? Если D является частным производным от B, то вы не должны использовать D как B извне.

person gpeche    schedule 17.09.2010
comment
+1 - Это был бы мой комментарий, в том числе к В чем смысл? вопрос. - person T.E.D.; 18.09.2010

Вы можете просто использовать приведение в стиле C. Нет необходимости в каких-либо «хаках» или «реализациях». Обертывание его в явную функцию служит людям, говорящим, что «приведение C-стиля — это плохо».

template<typename Targ, typename Src>
typename boost::enable_if<boost::is_base_of<Targ, Src>, Targ>::type &
private_cast(Src &src) { return (Targ&)src; }

Чтобы обеспечить безопасность приведения, вам нужно убедиться, что Targ на самом деле является частной или общедоступной базой. Это делает boost::is_base_of.


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


Определение оператора публичного кастинга не работает.

Для меня это не имеет смысла... Зачем тогда вообще делать базовый класс приватным? Просто сделайте это публичным. Причина, по которой ваши функции преобразования не работают, заключается в том, что стандарт требует, чтобы неявные преобразования никогда не учитывали функции преобразования в базовый класс, сам класс или void.

person Johannes Schaub - litb    schedule 17.09.2010
comment
Результат вашего приведения в стиле C (в данном случае эквивалентный reinterpret_cast) определяется реализацией, не так ли? - person avakar; 18.09.2010
comment
@avakar эквивалентен не reinterpret_cast, а static_cast с отключенной проверкой доступа (см. 5.4/7). - person Johannes Schaub - litb; 18.09.2010
comment
Боже мой, это правда! +1 от меня. - person avakar; 18.09.2010
comment
Обертывание его в явную функцию служит тому, что приведения в стиле C — это плохие люди: что-то не так с людьми, считающими, что приведения в стиле C в C++ неверны? Я имею в виду: у меня были ошибки, скрытые приведениями в стиле C, которые были бы обнаружены static_cast (приведение из Derived * к Base2 *, эти типы были объявлены только вперед). Я что-то пропустил? - person paercebal; 06.08.2014
comment
@paercebal они так же неправы и так же правы, как и люди, которые говорят, что синий — хороший цвет. Пишите то, что, по вашему мнению, наиболее удобно для чтения и сопровождения для вас и ваших коллег. В случае приведения к частному базовому классу я бы также обернул его в функцию, для записи. Но я не соглашусь с тем, что общие и бесконтекстные приведения в стиле C в C++ неверны. - person Johannes Schaub - litb; 06.08.2014
comment
@JohannesSchaub-litb: В моем случае меньше синего — это хорошо, а больше огня горит и причиняет боль. Пример (настоящей жизни): у вас есть класс Derived, нетривиально наследуемый от базового класса (например, все, что имеет адрес указателя this в Derived, отличающийся от адреса this в Base). По некоторым причинам существует множество опережающих объявлений, и в одном из таких источников указатель на Base преобразуется в указатель на Derived. Приведение в стиле C незаметно превратилось в повторное приведение (искажающее данные), в то время как класс в стиле C++ обнаружил бы проблему во время компиляции. - person paercebal; 10.08.2014
comment
@paercebal заявление «огонь горит и ранит» предполагает, что использование приведений в стиле c сжигает и вредит значительно больше, чем приведения в стиле c ++ в каждом сценарии. Но я так не думаю. ИМО, это утверждение является чрезмерным обобщением. Для приведения, включающего иерархию классов, я бы использовал static_cast или dynamic_cast в зависимости от обстоятельств. Во многих других случаях я бы использовал приведения в стиле C по мере необходимости. Я не думаю, что это сжигает и причиняет боль больше, чем использование приведения только в стиле C++ (наоборот, мне было бы больнее использовать везде приведения в стиле C++!). - person Johannes Schaub - litb; 11.08.2014

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

Я думаю, проще всего просто сделать функцию, которая возвращает базовый класс. Ниже я привожу полностью исправленную программу; пожалуйста, попробуйте скомпилировать свой код перед отправкой вопроса, поскольку использование идентификаторов, таких как «do», в качестве имен функций, вероятно, сделает недовольным каждый компилятор.. :-( :-(

class A {
  //base class                                                                                                                                                          
};

class AX : private A {
  //a child                                                                                                                                                             
 public:
  A *ToA() { return this; }
};

class AY : private A {
  //another specialized child                                                                                                                                           
 public:
  A *ToA() { return this; }
};

class B {
  //base class                                                                                                                                                          
 protected:
  void do_it (A a) {};
};

class BX : private B {
  //a child                                                                                                                                                             
  void do_it (AX a) {
    B::do_it( *a.ToA() );
  }
};

class BY : private B {
  //another specialized child                                                                                                                                           
  void do_it (AX a) {
    B::do_it( *a.ToA() );
  }
};

person systemBuilder    schedule 22.04.2015