Вызов конструктора базовых классов миксинов на основе количества аргументов
У меня есть два набора базовых классов миксинов, которые следуют следующему шаблону
// base class taking one contructor argument
struct OneArgBase
{
const double x;
template<typename T>
OneArgBase(const T & t) :
x(t.x)
{}
};
// base class taking two constructor arguments
struct TwoArgBase
{
const double y;
template<typename T, typename U>
TwoArgBase(const T & t, const U & u) :
y(t.y + u.y)
{}
};
Из этих базовых классов я получаю два набора классов миксинов
template<typename ... Mixins>
struct OneArgMix : Mixins...
{
template<typename T>
OneArgsMix(const T & t) :
Mixins(t)...
{}
};
template<typename ... Mixins>
struct TwoArgMix : Mixins...
{
template<typename T, typename U>
TwoArgsMix(const T & t, const U & u) :
Mixins(t, u)...
{}
};
Проблема, с которой я столкнулся сейчас, заключается в том, что я хочу передать классы, следующие за шаблоном OneArgBase, в TwoArgMix.
using Mix = TwoArgMix<TwoArgBase, OneArgBase>;
template<typename ... Mixins>
struct TwoArgMix : Mixins...
{
template<typename T, typename U>
TwoArgsMix(const T & t, const U & u) :
Mixins(t, u)... // if Mixins is TwoArgBase
Mixins(t)... // if Mixins is OneArgBase
{}
};
но понятия не имею, как двое пишут конструктор TwoArgMix таким образом, что if передает только первый параметр шаблона в базовые классы Mixin, которые следуют шаблону OneArgMix. Если возможно, я бы хотел избежать записи фиктивных аргументов в конструктор OneArgMix, потому что эти классы необходимы и для OneArgMix.
3 ответа
Все проблемы в программировании можно решить, добавив еще один уровень косвенности.
Нам нужно условно игнорировать второй аргумент, если он не может быть сконструирован. Один из способов сделать это - обернуть каждый миксин другим типом, который условно игнорирует его второй аргумент:
template <typename M>
struct WrappedMixin : M
{
template <typename T, typename U>
WrappedMixin(T const& t, U const& u)
: WrappedMixin(t, u, std::is_constructible<M, T const&, U const&>{})
{ }
private:
template <typename T, typename U>
WrappedMixin(T const& t, U const& u, std::true_type /* yes, use both */)
: M(t, u)
{ }
template <typename T, typename U>
WrappedMixin(T const& t, U const&, std::false_type /* no, just one */)
: M(t)
{ }
};
И теперь наш основной конструктор прост: вместо этого мы просто наследуем от обернутых:
template<typename ... Mixins>
struct TwoArgMix : WrappedMixin<Mixins>...
{
template<typename T, typename U>
TwoArgsMix(const T & t, const U & u)
: WrappedMixin<Mixins>(t, u)...
{ }
};
С factory вы можете сделать что-то вроде:
template <typename> struct Tag{};
template<typename T, typename U>
OneArgBase Create(Tag<OneArgBase>, const T& t, const U&)
{
return OneArgBase(t);
}
template<typename T, typename U>
TwoArgBase Create(Tag<TwoArgBase>, const T& t, const U& u)
{
return TwoArgBase(t, u);
}
template<typename ... Mixins>
struct TwoArgsMix : Mixins...
{
template<typename T, typename U>
TwoArgsMix(const T & t, const U & u) :
Mixins(Create(Tag<Mixins>{}, t, u))...
{}
};
Вы можете попробовать использовать частично указанный шаблон.
Что-то типа:
template<typename twoarg>
struct TwoArgMix<twoarg, OneArgBase>: ...
Это будет конкретная реализация с OneArgBase в качестве второго параметра шаблона.