Одним из преимуществ наследования для меня является то, что общедоступные методы записываются один раз в базовом классе, и каждый наследующий класс также автоматически имеет этот метод.
Теперь, например, скажем, я хочу предоставить каждому классу в моем программном обеспечении метод «своп». Класс, обеспечивающий эту функциональность, выглядит так:
template< typename... DataMembers >
class SwapMethodProvider
{
public:
inline void Swap( SwapMethodProvider& other );
private:
std::tuple< DataMembers... > member_dataMembers;
};
template< typename... DataMembers >
inline void SwapMethodProvider< DataMembers... >::Swap( SwapMethodProvider& other )
{
// Swap between the tuples that contain the data members
std::swap( member_dataMembers, other.member_dataMembers );
}
Теперь, если только один класс наследуется от этого, все хорошо. Но если я хочу, чтобы каждый класс в моем программном обеспечении наследовался от этого, то следующее не будет работать, потому что прямой базовый класс недоступен из-за неоднозначности:
class ExtraSauce : public SwapMethodProvider< int, double >
{};
class EvenMoreExtraSauce : public ExtraSauce, public SwapMethodProvider< int, double >
{};
Тогда первый класс «ExtraSauce» будет работать нормально, но «EvenMoreExtraSauce» не будет компилироваться. Теперь я понимаю, почему он не компилируется, но осталось сохранить экземпляр класса SwapMethodProvider в качестве члена данных. Моя проблема заключается только в повторяющемся коде, потому что каждый класс должен иметь свой собственный метод «Swap», который вызывает метод «Swap» экземпляра «SwapMethodProvider».
Во-первых, прав ли я до сих пор? Я хотел бы знать наверняка, что такое поведение невозможно.
Во-вторых, почему наследование так плохо поддерживается за одним уровнем? Так здорово один раз написать метод, а затем позволить всем объектам наследоваться от него. Даже если один и тот же объект появляется в классе более одного раза по-разному.
Предполагая, что он компилируется. Когда вы вызываете Swap на своем EvenMoreExtraSauce, какое поведение вы ожидаете?
Что-то вроде поведения конструктора, что может быть специальный метод, который вызывает один и тот же метод для всех базовых классов
Если вы не определили метод Swap в своем EvenMoreExtraSauce, который вызывает ExtraSauce::Swap, то/или SwapMethodProvider< std::string >::Swap. Как компилятор должен анализировать EvenMoreExtraSauce ::Swap ? это ExtraSauce::Swap или SwapMethodProvider<std::string>::Swap. В конструкторе по умолчанию он вызывает конструктор по умолчанию для каждого класса, но для метода он не может выбрать произвольный вариант.
Извините, у 2 «SwapMethodProvider» были разные аргументы шаблона, поэтому они были разными классами. Я починил это. Поскольку «EvenMoreExtraSauce» наследуется от всех, все методы подкачки базового класса можно просто вызывать с помощью указателя «this». То, что вы сказали, правильно
CRTP.
template <class Parent, typename... DataMembers>
class SwapMethodProvider { ... };
class ExtraSauce : public SwapMethodProvider <ExtraSauce, int, double> {};
class EvenMoreExtraSauce : public ExtraSauce,
public SwapMethodProvider<EvenMoreExtraSauce, int, double> {};
Большое спасибо!! Было весело проходить это с некоторой помощью
на самом деле это не так, что наследование плохо поддерживается, но само наследование в какой-то момент становится неприятным. Учтите, что наследование не является святым Граалем, и, возможно, взгляните на
std::swap
, на самом деле для замены произвольных типов наследование вообще не требуется.