为类型列表创建别名并将其作为模板参数传递

Nik*_*pol 7 c++ crtp variadic-templates c++11 c++14

我使用可变参数模板来实现访问者模式:

template<typename... Types>
class Visitor;

template<typename Type>
class Visitor<Type> {
    public:
        virtual void visit(Type &visitable) = 0;
};

template<typename Type, typename... Types>
class Visitor<Type, Types...>: public Visitor<Types...> {
    public:
        using Visitor<Types...>::visit;

        virtual void visit(Type &visitable) = 0;
};


template<typename... Types>
class VisitableInterface {
    public:
        virtual void accept(Visitor<Types...> &visitor) = 0;
};

template<typename Derived, typename... Types>
class Visitable : public VisitableInterface<Types...> {
    public:
        virtual void accept(Visitor<Types...> &visitor) {
            visitor.visit(static_cast<Derived&>(*this));
        }
};

class IntegerElement;
class StringElement;
class BoxElement;
class ImageElement;

class IntegerElement: public Visitable<IntegerElement, IntegerElement, StringElement, BoxElement,
    ImageElement> {};

class StringElement: public Visitable<StringElement, IntegerElement, StringElement, BoxElement,
    ImageElement> {};

class BoxElement: public Visitable<BoxElement, IntegerElement, StringElement, BoxElement,
    ImageElement> {};

class ImageElement: public Visitable<ImageElement, IntegerElement, StringElement, BoxElement,
    ImageElement> {};

class RenderEngine : public Visitor<IntegerElement, StringElement, BoxElement, ImageElement> 
{
    virtual void visit(IntegerElement& e) {};
    virtual void visit(StringElement& e) {};
    virtual void visit(BoxElement& e) {};
    virtual void visit(ImageElement& e) {};
};

int main(void)
{
    RenderEngine renderEngine;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

假设会有更多可访问的类,那么在继承VisitableVisitor模板时,最终会得到很长的类型列表.此外,如果要将LinkElement添加到此类访问者接受的可访问类型,则必须将其添加到任何位置.

由于在继承自Visitor和时使用了相同的类型列表Visitable(除了这个类型采用了添加类型,继承自它的类的类型),我想实现更优雅的解决方案.

是否有一种更优选,更清晰的方法来为除宏之外的此类型列表定义别名?

注意:通过宏我指的是定义和使用它而不是实际的列表:

#define VISITABLE_TYPES IntegerElement, StringElement, BoxElement, ImageElement
// Add more types here
Run Code Online (Sandbox Code Playgroud)

max*_*x66 2

std::tupleusing你的朋友。

如果你Visitable这样定义

template <typename, typename>
class Visitable;

template<typename Derived, typename... Types>
class Visitable<Derived, std::tuple<Types...>> : public VisitableInterface<Types...> {
    public:
        virtual void accept(Visitor<Types...> &visitor) {
            visitor.visit(static_cast<Derived&>(*this));
        }
};
Run Code Online (Sandbox Code Playgroud)

并添加 via 来using替代宏观想法

using tupleT = std::tuple<IntegerElement, StringElement, BoxElement, ImageElement>;
Run Code Online (Sandbox Code Playgroud)

元素的定义变得简单

class IntegerElement: public Visitable<IntegerElement, tupleT> {};
class StringElement: public Visitable<StringElement, tupleT> {};
class BoxElement: public Visitable<BoxElement, tupleT> {};
class ImageElement: public Visitable<ImageElement, tupleT> {};
Run Code Online (Sandbox Code Playgroud)

你的例子修改了

#include <iostream>

template<typename... Types>
class Visitor;

template<typename Type>
class Visitor<Type> {
    public:
        virtual void visit(Type &visitable) = 0;
};

template<typename Type, typename... Types>
class Visitor<Type, Types...>: public Visitor<Types...> {
    public:
        using Visitor<Types...>::visit;

        virtual void visit(Type &visitable) = 0;
};


template<typename... Types>
class VisitableInterface {
    public:
        virtual void accept(Visitor<Types...> &visitor) = 0;
};

template <typename, typename>
class Visitable;

template<typename Derived, typename... Types>
class Visitable<Derived, std::tuple<Types...>> : public VisitableInterface<Types...> {
    public:
        virtual void accept(Visitor<Types...> &visitor) {
            visitor.visit(static_cast<Derived&>(*this));
        }
};

class IntegerElement;
class StringElement;
class BoxElement;
class ImageElement;

using tupleT = std::tuple<IntegerElement, StringElement, BoxElement, ImageElement>;

class IntegerElement: public Visitable<IntegerElement, tupleT> {};
class StringElement: public Visitable<StringElement, tupleT> {};
class BoxElement: public Visitable<BoxElement, tupleT> {};
class ImageElement: public Visitable<ImageElement, tupleT> {};

class RenderEngine : public Visitor<IntegerElement, StringElement, BoxElement, ImageElement> 
{
   public:
    virtual void visit(IntegerElement& e) { std::cout << "visit Int\n"; };
    virtual void visit(StringElement& e) { std::cout << "visit Str\n"; };
    virtual void visit(BoxElement& e) { std::cout << "visit Box\n"; };
    virtual void visit(ImageElement& e) { std::cout << "visit Img\n"; };
};

int main(void)
{
    RenderEngine renderEngine;

    IntegerElement  intE;
    StringElement   strE;
    BoxElement      boxE;
    ImageElement    imgE;

    renderEngine.visit(intE);
    renderEngine.visit(strE);
    renderEngine.visit(boxE);
    renderEngine.visit(imgE);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

- - 编辑 - -

我尝试回复您的评论问题

为什么模板类是Visible;在定义实际模板之前需要吗?

我不知道是否可以以更简单的方式做到这一点,但是......这是因为我们需要从std::tuple. 因此,您需要一个通用定义(template <typename, typename>以便能够接收std::tuple<something>类型,并且需要专门化以便可以提取someting类型。

通过定义一个采用 std::tuple 作为模板参数的附加模板,也可以为 Visitor 模板完成同样巧妙的技巧。您也可以将其添加到您的答案中吗?

是的,这是可能的。

VisitableInterface但你也必须修改RenderEngine

一点改进带来的大改变(恕我直言);仅用于tupleT定义RenderEngine.

不管怎样,你的例子变成

#include <iostream>

template<typename>
class Visitor;

template<typename Type>
class Visitor<std::tuple<Type>> {
    public:
        virtual void visit(Type &visitable) = 0;
};

template<typename Type, typename... Types>
class Visitor<std::tuple<Type, Types...>>: public Visitor<std::tuple<Types...>> {
    public:
        using Visitor<std::tuple<Types...>>::visit;

        virtual void visit(Type &visitable) = 0;
};

template<typename... Types>
class VisitableInterface {
    public:
        virtual void accept(Visitor<std::tuple<Types...>> &visitor) = 0;
};

template <typename, typename>
class Visitable;

template<typename Derived, typename... Types>
class Visitable<Derived, std::tuple<Types...>> : public VisitableInterface<Types...> {
    public:
        virtual void accept(Visitor<std::tuple<Types...>> &visitor) {
            visitor.visit(static_cast<Derived&>(*this));
        }
};

class IntegerElement;
class StringElement;
class BoxElement;
class ImageElement;

using tupleT = std::tuple<IntegerElement, StringElement, BoxElement, ImageElement>;

class IntegerElement: public Visitable<IntegerElement, tupleT> {};
class StringElement: public Visitable<StringElement, tupleT> {};
class BoxElement: public Visitable<BoxElement, tupleT> {};
class ImageElement: public Visitable<ImageElement, tupleT> {};

class RenderEngine : public Visitor<tupleT> 
{
   public:
    virtual void visit(IntegerElement& e) { std::cout << "visit Int\n"; };
    virtual void visit(StringElement& e) { std::cout << "visit Str\n"; };
    virtual void visit(BoxElement& e) { std::cout << "visit Box\n"; };
    virtual void visit(ImageElement& e) { std::cout << "visit Img\n"; };
};

int main(void)
{
    RenderEngine renderEngine;

    IntegerElement  intE;
    StringElement   strE;
    BoxElement      boxE;
    ImageElement    imgE;

    renderEngine.visit(intE);
    renderEngine.visit(strE);
    renderEngine.visit(boxE);
    renderEngine.visit(imgE);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)