提问人:user3814483 提问时间:8/26/2023 最后编辑:user3814483 更新时间:8/26/2023 访问量:76
Mixin 设计,可为各种应用扩展模板参数结构
Mixin design to extend template parameter struct for various applications
问:
我有一个接受结构作为参数的模板化类(这里有一个原因,我不会在这里讨论,但设计需要它)。我正在尝试生成一个可以与此类一起使用的 mixin 设计模式(静态装饰器),以获得最大的灵活性和可扩展性,但我似乎陷入了模板地狱,编译器消息晦涩难懂,无法确定问题所在。
我有一个这样的类:
// 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
添加了 typedef,以便派生类(即 mixin)“知道”使用或打算/希望用于基类的类型,以促进可扩展性。using ...
现在,我想为可以混合/匹配组合的混合蛋白定义一下,因此尝试可能如下所示:Derived
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> > >;
此外,我什至不能省略上面的第 2 个和第 3 个参数,因为编译器抱怨我在需要 1 个模板参数时提供了 3 个模板参数。它似乎忽略了默认参数,原因我还不明白。mixin
(2) 除非我弄错了,否则上述设计不利于在 mixin 包装类上包装第二个 mixin。也就是说,由于模板参数的数量不同,typedef mixin 不能接受另一种以相同方式设计的混合,而必须直接应用于 。Derived
D
Derived
任何想法或指示(没有双关语!)都是值得赞赏的。
答:
我不完全理解您在第二个片段中试图做什么,但这是我对编译时混合的替代方法。
每种混合蛋白的形式包括:
template <typename Derived, typename Base>
struct MyMixin : Base {};
我们必须继承的链中的下一个 mixin 在哪里(或者,对于最终的 mixin,我们正在自定义的原始类),并且是继承所有 mixin 的最终类。Base
Derived
首先,一个 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();
}
};
};
由于您需要从依赖于 mixin 的基础继承,因此我创建了一个嵌套类,并删除了外部类的构造函数/析构函数,因此它仅用作容器。如果不是基类,您可能可以直接使用外部类。
请注意,不要接受来自 mixin 的这些类的覆盖。typename Derived::X
X
还有一个小包装器应用于这个类: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();
}
上一个:通过从基本模板继承来扩展模板
下一个:静态断言基类模板的继承
评论
Base
mixin
Derived<X,Y>
Derived
mixin_x< Derived<X,Y> >
mixin
MixinX
)X
Y
Base