简体   繁体   English

使用可变参数模板的递归继承

[英]Recursive inheritance with variadic templates

Consider the following code: 请考虑以下代码:

#include <iostream>

struct ActionOption {
    virtual void foo(int) const = 0;
};

template <int> struct ActionType;

template <> struct ActionType<0> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<0>::foo(int) called.\n";}
};

template <> struct ActionType<1> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<1>::foo(int) called.\n";} 
};

template <> struct ActionType<2> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";}
};

template <> struct ActionType<3> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";}
};

template <> struct ActionType<4> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<4>::foo(int) called.\n";}
};

template <int...> struct PossibleActions;

template <> struct PossibleActions<> { void operator()(int) const {} };

template <int First, int... Rest>
struct PossibleActions<First, Rest...> : ActionType<First>, PossibleActions<Rest...> {
    void operator()(int a) const {
        ActionType<First>::foo(a);
        PossibleActions<Rest...>::operator()(a);
    }
};

// Anything that can call ActionType<2>::foo(int) can also call ActionType<3>::foo(int).
struct Object : PossibleActions<1,  2,3,  4> {
    void foo(int a) {PossibleActions<1,2,3,4>()(a);}
};

struct Blob : PossibleActions<0,  2,3,  4> {
    void foo(int a) {PossibleActions<0,2,3,4>()(a);}
};

int main() {
    Object object;
    object.foo(12);  // ActionType<1>::foo(int) called  ActionType<2>::foo(int) called   ActionType<3>::foo(int) called  ActionType<4>::foo(int) called
    std::cout << std::endl;

    Blob blob;
    blob.foo(12);  // ActionType<0>::foo(int) called  ActionType<2>::foo(int) called   ActionType<3>::foo(int) called  ActionType<4>::foo(int) called
    std::cout << std::endl;
}

It runs except here is the problem: anything that can call ActionType<2>::foo(int) can also call ActionType<3>::foo(int) . 它运行除了这里是问题:任何可以调用ActionType<2>::foo(int)也可以调用ActionType<3>::foo(int) Thus every time I define a new class, if I use 2 or 3 I have to use both in PossibleActions<I...> . 因此,每次我定义一个新类时,如果我使用2或3,我必须在PossibleActions<I...>使用它们。 This is problematic for maintenance of course (say I decide in the future that using 2 must also use 3, 7, and 20). 这对于维护当然是有问题的(比如说我将来决定使用2还必须使用3,7和20)。 The following solution: 以下解决方案:

using TwoAndThree = PossibleActions<2,3>;
struct Object : PossibleActions<1,4>, TwoAndThree {
    void foo(int a) {PossibleActions<1,4>()(a);  TwoAndThree()(a);}
};

struct Blob : PossibleActions<0,4>, TwoAndThree {
    void foo(int a) {PossibleActions<0,4>()(a);  TwoAndThree()(a);}
};

is not acceptable because I need ActionType<N>::foo(int) called in numerical order . 是不可接受的,因为我需要按数字顺序调用ActionType<N>::foo(int) Splitting PossibleActions<1,4>()(a); 分裂PossibleActions<1,4>()(a); is a very is poor solution too because it runs into the same maintenaince problem (makes maintenance even worse I think). 是一个非常糟糕的解决方案,因为它遇到了相同的维护问题(我认为维护更糟糕)。

template <> struct ActionType<2> : ActionOption { virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";} };
template <> struct ActionType<3> : ActionType<2> { virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";} };

does not compile due to ambiguity (and using virtual inheritance did not help), and I can't think of anything else. 由于歧义而无法编译(并且使用虚拟继承没有帮助),我想不出别的什么。 Is there a solution to this problem? 有这个问题的解决方案吗?

Perhaps redefine PossibleActions with template <typename... Args> struct PossibleActions; 也许用template <typename... Args> struct PossibleActions;重新定义template <typename... Args> struct PossibleActions; ? But then the recursion is lost. 但随后失去了递归。

Or is it? 或者是吗?

Related question: Is there a way to carry out recursion with Args... where some types are int but some are not (and with those that are not use recursion with the ints that define those types)? 相关问题:有没有办法用Args执行递归...其中一些类型是int但有些类型不是(以及那些不使用递归定义这些类型的int)? For example 例如

PossibleActions<1, TwoAndThree, 4, EightAndTen, 20>()(a);

iterates through 1,2,3,4,8,10,20 as desired because TwoAndThree = PossibleActions<2,3> and EightAndTen = PossibleActions<8,10> ??? 根据需要迭代1,2,3,4,8,10,20,因为TwoAndThree = PossibleActions<2,3>EightAndTen = PossibleActions<8,10> ??? If possible, that would solve the problem. 如果可能,那将解决问题。

Credit goes to Piotr. 归功于Piotr。 S for this solution (I wish I could offer him points, but he likes to hide his amazingness for some reason). S这个解决方案(我希望我可以给他积分,但他喜欢隐藏他的惊人因为某些原因)。 Though his second solution is nice too, I prefer the syntax offered by his first solution. 虽然他的第二个解决方案也很好,但我更喜欢他的第一个解决方案提供的语法。 His Sort struct had to be generalized with 他的Sort结构必须用

template <typename, typename...> struct Sort;

template <typename T, typename A, typename B>
struct Sort<T,A,B> {
    using type = typename Merge<T,A,B>::type;
};

template <typename T, typename First, typename Second, typename... Rest>
struct Sort<T, First, Second, Rest...> {
    using type = typename Sort<T, typename Sort<T, First, Second>::type, Rest...>::type;
};

so I did that for him. 所以我为他做了那件事。 This allows the syntax 这允许语法

struct Widget : Sort<PossibleActions<0,5>, OneAndFour, TwoAndThree>

which I like better. 我更喜欢。 I added template-templates into the picture too: 我也在模板中添加了模板模板:

#include <iostream>

namespace Detail {
    template <typename T, typename, typename, T...> struct Merge;

    template <typename T, template <T...> class S, T... Ks>
    struct Merge<T, S<>, S<>, Ks...> {
        using type = S<Ks...>;
    };

    template <typename T, template <T...> class S, T... Is, T... Ks>
    struct Merge<T, S<Is...>, S<>, Ks...> {
        using type = S<Ks..., Is...>;
    };

    template <typename T, template <T...> class S, T... Js, T... Ks>
    struct Merge<T, S<>, S<Js...>, Ks...> {
        using type = S<Ks..., Js...>;
    };

    template <typename T, bool, typename, typename, T...> struct Strip;

    template <typename T, template <T...> class S, T I, T... Is, T J, T... Js, T... Ks>
    struct Strip<T, true, S<I, Is...>, S<J, Js...>, Ks...> {
        using type = Merge<T, S<I, Is...>, S<Js...>, Ks..., J>;
    };

    template <typename T, template <T...> class S, T I, T... Is, T J, T... Js, T... Ks>
    struct Strip<T, false, S<I, Is...>, S<J, Js...>, Ks...> {
        using type = Merge<T, S<Is...>, S<J, Js...>, Ks..., I>;
    };

    template <typename T, template <T...> class S, T I, T... Is, T J, T... Js, T... Ks>
    struct Merge<T, S<I, Is...>, S<J, Js...>, Ks...> : Strip<T, (I > J), S<I, Is...>, S<J, Js...>, Ks...>::type {};

    template <typename, typename...> struct Sort;

    template <typename T, typename A, typename B>
    struct Sort<T,A,B> {
        using type = typename Merge<T,A,B>::type;
    };

    // Piotr S.'s Sort generalized to accept any number of template arguments.
    template <typename T, typename First, typename Second, typename... Rest>
    struct Sort<T, First, Second, Rest...> {
        using type = typename Sort<T, typename Sort<T, First, Second>::type, Rest...>::type;
    };
}

template <typename... P>
using Sort = typename Detail::Sort<int, P...>::type;

struct ActionOption {
    virtual void foo(int) const = 0;
};

template <int> struct ActionType;

template <> struct ActionType<0> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<0>::foo(int) called.\n";}
};

template <> struct ActionType<1> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<1>::foo(int) called.\n";} 
};

template <> struct ActionType<2> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";}
};

template <> struct ActionType<3> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";}
};

template <> struct ActionType<4> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<4>::foo(int) called.\n";}
};

template <> struct ActionType<5> : ActionOption {
    virtual void foo(int) const override {std::cout << "ActionType<5>::foo(int) called.\n";}
};

template <int...> struct PossibleActions;

template <> struct PossibleActions<> { void operator()(int) const {} };

template <int First, int... Rest>
struct PossibleActions<First, Rest...> : ActionType<First>, PossibleActions<Rest...> {
    void operator()(int a) const {
        ActionType<First>::foo(a);
        PossibleActions<Rest...>::operator()(a);
    }
};

using OneAndFour = PossibleActions<1,4>;
using TwoAndThree = PossibleActions<2,3>;

struct Thing : PossibleActions<0,1,2,3,4> {
    void foo(int a) {PossibleActions<0,1,2,3,4>::operator()(a);}
};

struct Object : Sort<PossibleActions<1,4>, TwoAndThree> {
    void foo(int a) {Sort<PossibleActions<1,4>, TwoAndThree>()(a);}
};

struct Blob : Sort<PossibleActions<0,4>, TwoAndThree> {
    void foo(int a) {Sort<PossibleActions<0,4>, TwoAndThree>()(a);}
};

struct Widget : Sort<PossibleActions<0,5>, OneAndFour, TwoAndThree> {
    void foo(int a) {Sort<PossibleActions<0,5>, OneAndFour, TwoAndThree>()(a);} 
};

int main() {
    Thing thing;
    thing.foo(12);  // ActionType<0>::foo(int)  ActionType<1>::foo(int) called  ActionType<2>::foo(int) called   ActionType<3>::foo(int) called  ActionType<4>::foo(int) called
    std::cout << std::endl;

    Object object;
    object.foo(12);  // ActionType<1>::foo(int) called  ActionType<2>::foo(int) called   ActionType<3>::foo(int) called   ActionType<4>::foo(int) called
    std::cout << std::endl;

    Blob blob;
    blob.foo(12);  // ActionType<0>::foo(int) called  ActionType<2>::foo(int) called   ActionType<3>::foo(int) called   ActionType<4>::foo(int) called
    std::cout << std::endl;

    Widget widget;
    widget.foo(12);  // ActionType<0>::foo(int) called  ActionType<1>::foo(int) called   ActionType<2>::foo(int) called   ActionType<3>::foo(int) called   ActionType<4>::foo(int) called   ActionType<5>::foo(int) called
}

Note however, the solution actually fails if the original packs are themselves not sorted. 但请注意,如果原始包本身未排序,则解决方案实际上会失败。 This will probably require a helper sorter struct to use first on the original packs before carrying out the above. 在执行上述操作之前,这可能需要帮助分拣机结构首先在原始包上使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM