我有一个接受结构体作为参数的模板类(这是有原因的,我不会在这里讨论,但设计需要它)。我正在尝试生成一个可与此类一起使用的混合设计模式(静态装饰器),以实现最大的灵活性和可扩展性,但我似乎陷入了模板地狱,编译器消息晦涩难懂,无法确定问题。
我有一堂这样的课:
// User extensible struct
struct X {
...
};
struct Y {
....
};
template <class T = X, class U = Y>
class Derived : public Base<T,U> // This inheritance structure is a requirement.
{
...
using x_type = T;
using y_type = U;
};
这个想法是,人们可以继承此类并默认使用
X
和 Y
,或者从 X
和 Y
派生的自定义结构,提供附加属性或功能。这一切都很好。
添加了
using ...
typedef,以便派生类(即 mixin)“知道”基类使用或打算/希望使用什么类型,以促进可扩展性。
现在,我想为
Derived
定义可以混合/匹配组合的 mixin,因此尝试可能如下所示:
template <class D>
struct mixin_x : public D::x_type {
...
};
template <class D>
struct mixin_y : public D::y_type {
...
};
template < template <class, class> class D, class X, class Y,
class MixinX = mixin_x< D<X,Y> >,
class MixinY = mixin_y< D<X,Y> > >
using mixin = D<MixinX,MixinY>;
当头文件包含在编译单元中时,上面的代码就会编译。但是,有两个问题:
(1) 模板错误导致“模板参数 1 无效”
例如,以下代码会生成上述错误:
mixin< Derived<X,Y>, mixin_x< Derived<X,Y> >, mixin_y< Derived<X,Y> > >;
此外,我什至不能省略上面
mixin
的第二个和第三个参数,因为编译器抱怨我在需要 3 个模板参数时提供了 1 个模板参数。它似乎忽略了默认参数,原因我还不明白。
(2) 除非我弄错了,否则上述设计并不有助于在 mixin 包装的
Derived
类上包装第二个 mixin。也就是说,由于模板参数的数量不同,typedef mixin 不能接受 D
是另一个以相同方式设计的混合,而必须直接应用于 Derived
。
任何想法或指示(没有双关语!)表示赞赏。
我不完全理解你在第二个片段中想要做什么,但这是我编译时混合的替代方法。
每个 mixin 都采用以下形式:
template <typename Derived, typename Base>
struct MyMixin : Base {};
其中
Base
是我们必须继承的链中的下一个mixin(或者,对于最终的mixin,我们正在自定义的原始类),Derived
是继承所有mixin的最终类。
首先,一个 mixin 组合器:
template <typename Derived, typename Base, template <typename, typename> typename ...M>
struct CombineMixins
{
using type = Base;
};
template <typename Derived, typename Base, template <typename, typename> typename M0, template <typename, typename> typename ...M>
struct CombineMixins<Derived, Base, M0, M...>
{
using NextBase = typename CombineMixins<Derived, Base, M...>::type;
using type = M0<Derived, NextBase>;
static_assert(std::derived_from<type, NextBase>, "Mixins must inherit from their second template parameters.");
};
给定一个 mixins 列表,链中的最后一个类(我们正在自定义的)和最派生的类(传递给 mixins),将相互继承。
然后是我们自定义的类:(我已将所有内容移至单个类中,因此您无需分别将 mixin 应用于
X
和 Y
)
template <typename Derived>
struct DefaultFoo
{
DefaultFoo() = delete;
~DefaultFoo() = delete;
struct X
{
void foo() {std::cout << "I'm X\n";}
};
struct Y
{
void foo() {std::cout << "I'm Y\n";}
};
struct Impl : Base<typename Derived::X, typename Derived::Y>
{
void run()
{
typename Derived::X x;
x.foo();
std::cout << "---\n";
typename Derived::Y y;
y.foo();
}
};
};
由于您需要从依赖于 mixins 的基类继承,因此我创建了一个嵌套类,并删除了外部类的构造函数/析构函数,因此它仅充当容器。如果不是基础类,你可能可以直接使用外部类。
注意
typename Derived::X
而不是 X
接受来自 mixin 的这些类的覆盖。
还有一个小包装,用于
CombineMixins
应用于此类:
template <typename Derived, template <typename/*Derived*/, typename/*NextBase*/> typename ...Mixins>
struct BasicFoo : CombineMixins<Derived, DefaultFoo<Derived>, Mixins...>::type {};
使用方法如下:
template <typename Derived, typename Base>
struct X_mixin : Base
{
struct X : Base::X
{
void foo()
{
Base::X::foo();
std::cout << "I'm X_mixin::X\n";
}
};
};
template <typename Derived, typename Base>
struct Y_mixin : Base
{
struct Y : Base::Y
{
void foo()
{
Base::Y::foo();
std::cout << "I'm Y_mixin::Y\n";
}
};
};
struct MyFoo : BasicFoo<MyFoo, X_mixin, Y_mixin> {};
int main()
{
MyFoo::Impl foo;
foo.run();
}
最终代码:在gcc.godbolt.org上运行