у меня есть шаблон класса roundtrip
который занимает две политики. Пока они разные, все нормально, но использование одной политики дважды приводит к ошибкам компиляции.
Пример:
#include <iostream>
class walk {
protected:
void move() {
std::cout<<"i'm walking."<<std::endl;
}
};
class car {
protected:
void move() {
std::cout<<"i'm driving in a car."<<std::endl;
}
};
template<typename S, typename T>
class roundtrip : private S, private T {
public:
void printSchedule(void) {
std::cout<<"away: ";
S::move();
std::cout<<"return: ";
T::move();
}
};
int main(void){
roundtrip<walk,car> LazyTrip;
LazyTrip.printSchedule();
roundtrip<car,car> VeryLazyTrip; // ERROR: error: duplicate base type ‘walk’ invalid
VeryLazyTrip.printSchedule();
return 0;
}
как это можно решить? или есть лучший дизайн для достижения того же поведения?
РЕДАКТИРОВАТЬ: я добавил оболочки для политик, пользовательский интерфейс не меняется. Что вы думаете об этом решении, это чистый дизайн?
template<typename T>
class outbound : private T {
protected:
void moveOutbound(void) {
T::move();
}
};
template<typename T>
class inbound : private T {
protected:
void moveInbound(void) {
T::move();
}
};
template<typename S, typename T>
class roundtrip : private outbound<S>, private inbound<T> {
public:
void printSchedule(void) {
std::cout<<"away: ";
this->moveOutbound();
std::cout<<"return: ";
this->moveInbound();
}
};
Вместо того, чтобы наследовать, вы можете добавить членов данных в roundtrip
, но функции в walk
а также car
в настоящее время protected
, Если вы не можете сделать функции в walk
а также car
общественности, вы могли бы:
подружиться с roundtrip
шаблон
class walk {
protected:
void move() {
std::cout<<"i'm walking."<<std::endl;
}
template<class T, class S>
friend class roundtrip;
};
сделать функции-члены доступными для roundtrip
через вспомогательный класс:
template<typename S, typename T>
class roundtrip {
private:
struct helperT : private T
{
public: // or private + friend roundtrip
using T::move;
using T::T;
} mT;
struct helperS : private S
{
public:
using S::move;
using S::S;
} mS;
public:
void printSchedule(void) {
std::cout<<"away: ";
mT.move();
std::cout<<"return: ";
mS.move();
}
};
Если интерфейс одинаков для обоих типов, вы можете использовать шаблон вспомогательного класса вместо двух вспомогательных классов:
template<typename S, typename T>
class roundtrip {
private:
template<class U>
struct helper : private U
{
public: // or private + friend roundtrip
using U::move;
using U::U;
};
helper<S> mS;
helper<T> mT;
public:
void printSchedule(void) {
std::cout<<"away: ";
mT.move();
std::cout<<"return: ";
mS.move();
}
};
(также возможно наследовать от вспомогательного шаблона специализации, например, class roundtrip : helperT<T>, helperS<S>
)
Как заметил DyP достаточно:
template<typename S, typename T>
class roundtrip {
public:
void printSchedule(void) {
std::cout<<"away: ";
awayPolicy.move();
std::cout<<"return: ";
returnPolicy.move();
}
private:
T awayPolicy;
S returnPolicy;
};
Но это требует move
методы public
, Вы также можете сделать их static
а также public
, так что вам не понадобятся поля экземпляра:
class walk {
public:
static void move() {
std::cout<<"i'm walking."<<std::endl;
}
};
// the same for car
template<typename S, typename T>
class roundtrip {
public:
void printSchedule(void) {
std::cout<<"away: ";
S::move();
std::cout<<"return: ";
T::move();
}
};
Вы также можете рассмотреть возможность создания базового класса:
class travelPolicy
{
public:
virtual void move() = 0;
//...
};
с car
а также walk
исходя из этого. Тогда ваш roundtrip
класс может принимать обе политики через конструктор и использовать их в printSchedule
с помощью указателей.
class roundtrip
{
public:
roundtrip(
std::shared_ptr<travelpolicy> awayPolicy,
std::shared_ptr<travelpolicy> returnPolicy)
{
this->awayPolicy = awayPolicy;
this->returnPolicy = returnPolicy;
}void printSchedule(void)
{
std::cout<<"away: ";
awayPolicy->move();
std::cout<<"return: ";
returnPolicy->move();
}
private:
std::shared_ptr<travelPolicy> awayPolicy;
std::shared_ptr<travelPolicy> returnPolicy;
};
ОБНОВИТЬ (отвечая на редактирование ОП):
Ваше решение в целом хорошо, однако оно все еще кажется чрезмерным наследованием. когда A
наследуется B
это способ сказать, что B
«s являются A
s. Здесь, конечно, это не так. «Взлом» из частного наследства делает эту неловкость невидимой, поэтому она кажется мне приемлемой.
Почему бы не специализироваться на случае идентичных базовых классов?
Если бы вы могли, я бы использовал mpl :: if_ boost вместо моего и c ++ 11 или type_trait boost is_same.
У меня нет удобного компилятора, поэтому ниже могут быть проблемы с синтаксисом
#include <type_traits>
template<typename S, typename T>
class roundtrip_diff : private S, private T {
public:
void printSchedule(void) {
std::cout<<"away: ";
S::move();
std::cout<<"return: ";
T::move();
}
};
template<typename S>
class roundtrip_same : private S {
public:
void printSchedule(void) {
std::cout<<"away: ";
S::move();
std::cout<<"return: ";
S::move();
}
};
template<bool, typename True, typename False> struct if_{ typedef True type; };
template<typename True, typename False> struct if_<false,True,False> { typedef False type; };
template<typename A, typename B> struct is_same { enum{value=0}; };
template<typename A> struct is_same<A,A> { enum{value=1}; };
template<typename S, typename T>
class roundtrip : if_< is_same<S,T>::value, roundtrip_same<S>, roundtrip_diff<S,T> >::type { };
Очевидно, что нужно найти более элегантное решение, если вы разрешите произвольное количество аргументов, но я думаю, что это должно устранить вашу ошибку дублирующих баз.