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

Mixin design to extend template parameter struct for various applications

提问人:user3814483 提问时间:8/26/2023 最后编辑:user3814483 更新时间:8/26/2023 访问量:76

问:

我有一个接受结构作为参数的模板化类(这里有一个原因,我不会在这里讨论,但设计需要它)。我正在尝试生成一个可以与此类一起使用的 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;
};

这个想法是,人们可以继承这个类,并默认使用和,或者从和派生的自定义结构,提供额外的属性或函数。这一切都很好。XYXY

添加了 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 不能接受另一种以相同方式设计的混合,而必须直接应用于 。DerivedDDerived

任何想法或指示(没有双关语!)都是值得赞赏的。

C++ 模板 继承装饰 mixins

评论

0赞 Osyotr 8/26/2023
我不清楚你想做什么,但我看到两个问题:缺少尖括号和模板模板参数无效。godbolt.org/z/P8re3hPqM
0赞 user3814483 8/26/2023
关于尖括号,这是帖子中的错别字,而不是实际代码(已修复)。无效的模板模板参数在哪里?希望从解释中可以清楚地看出我想做什么。
0赞 HolyBlackCat 8/26/2023
我不明白你想做什么,从第二个片段开始。另外,请提供一个最小的可重现示例,我们可以编译并看到相同的错误。另外,为什么要提到?Base
0赞 Osyotr 8/26/2023
@user3814483期望可以与两个模板参数一起使用的东西作为第一个参数,那么您传递了一些甚至不是模板的东西(即您使用了 而不是 )。接下来,不清楚为什么你已经在模板中作为mixinDerived<X,Y>Derivedmixin_x< Derived<X,Y> >mixinMixinX)
1赞 HolyBlackCat 8/26/2023
我只理解想要编译时混合的一般想法,但我还不明白第二个片段背后的想法是什么,或者它是如何实现的。“DefaultFoo 继承自另一个类”哦,我现在明白了。然后,我将使用外部类作为容器来容纳 , 和 实际的类派生自并执行以下操作: gcc.godbolt.org/z/cn1njYqeGXYBase

答:

1赞 HolyBlackCat 8/26/2023 #1

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

每种混合蛋白的形式包括:

template <typename Derived, typename Base>
struct MyMixin : Base {};

我们必须继承的链中的下一个 mixin 在哪里(或者,对于最终的 mixin,我们正在自定义的原始类),并且是继承所有 mixin 的最终类。BaseDerived

首先,一个 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 应用于 和XY)

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::XX

还有一个小包装器应用于这个类: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();
}

最终代码:run on gcc.godbolt.org