簡體   English   中英

使用可變參數模板的遞歸繼承

[英]Recursive inheritance with variadic templates

請考慮以下代碼:

#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;
}

它運行除了這里是問題:任何可以調用ActionType<2>::foo(int)也可以調用ActionType<3>::foo(int) 因此,每次我定義一個新類時,如果我使用2或3,我必須在PossibleActions<I...>使用它們。 這對於維護當然是有問題的(比如說我將來決定使用2還必須使用3,7和20)。 以下解決方案:

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);}
};

是不可接受的,因為我需要按數字順序調用ActionType<N>::foo(int) 分裂PossibleActions<1,4>()(a); 是一個非常糟糕的解決方案,因為它遇到了相同的維護問題(我認為維護更糟糕)。

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";} };

由於歧義而無法編譯(並且使用虛擬繼承沒有幫助),我想不出別的什么。 有這個問題的解決方案嗎?

也許用template <typename... Args> struct PossibleActions;重新定義template <typename... Args> struct PossibleActions; 但隨后失去了遞歸。

或者是嗎?

相關問題:有沒有辦法用Args執行遞歸...其中一些類型是int但有些類型不是(以及那些不使用遞歸定義這些類型的int)? 例如

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

根據需要迭代1,2,3,4,8,10,20,因為TwoAndThree = PossibleActions<2,3>EightAndTen = PossibleActions<8,10> ??? 如果可能,那將解決問題。

歸功於Piotr。 S這個解決方案(我希望我可以給他積分,但他喜歡隱藏他的驚人因為某些原因)。 雖然他的第二個解決方案也很好,但我更喜歡他的第一個解決方案提供的語法。 他的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;
};

所以我為他做了那件事。 這允許語法

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

我更喜歡。 我也在模板中添加了模板模板:

#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
}

但請注意,如果原始包本身未排序,則解決方案實際上會失敗。 在執行上述操作之前,這可能需要幫助分揀機結構首先在原始包上使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM