Mixin 设计为各种应用扩展模板参数结构

问题描述 投票:0回答:1

我有一个接受结构体作为参数的模板类(这是有原因的,我不会在这里讨论,但设计需要它)。我正在尝试生成一个可与此类一起使用的混合设计模式(静态装饰器),以实现最大的灵活性和可扩展性,但我似乎陷入了模板地狱,编译器消息晦涩难懂,无法确定问题。

我有一堂这样的课:

// 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

任何想法或指示(没有双关语!)表示赞赏。

c++ templates inheritance decorator mixins
1个回答
0
投票

我不完全理解你在第二个片段中想要做什么,但这是我编译时混合的替代方法。

每个 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上运行

© www.soinside.com 2019 - 2024. All rights reserved.