многократное использование политик в разработке на основе политик

у меня есть шаблон класса 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();
}
};

1

Решение

Вместо того, чтобы наследовать, вы можете добавить членов данных в 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>)

2

Другие решения

Как заметил 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 являются As. Здесь, конечно, это не так. «Взлом» из частного наследства делает эту неловкость невидимой, поэтому она кажется мне приемлемой.

1

Почему бы не специализироваться на случае идентичных базовых классов?
Если бы вы могли, я бы использовал 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 { };

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

0
По вопросам рекламы [email protected]