为类型列表创建别名并将其作为模板参数传递 [英] Create alias for a list of types and passing it as a template parameter

查看:71
本文介绍了为类型列表创建别名并将其作为模板参数传递的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用可变参数模板来实现访客模式:

I am using variadic templates to implement the visitor pattern:

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

假设会有更多可访问的课程,您最终会得到一个非常从 Visitable Visitor 模板继承时的一长串类型。另外,如果要将LinkElement添加到这种访问者接受的可访问类型中,则必须将其添加到任何地方。

Assuming there will be more classes which are visitable, you end up with a very long list of types when inheriting from the Visitable and Visitor templates. Also, if you want to add LinkElement to the visitable types accepted by this kind of visitor, you have to add it everywhere.

由于在使用相同类型的列表时从 Visitor Visitable 继承(除了该对象采用加法类型,即继承的类的类型)

Since the same list of types is used when inheriting from the Visitor and Visitable (except that this one takes the addition type, the type of the class which is inheriting from it), I would like to implement a more elegant solution.

有没有一种更优选,更简洁的方法为此类型列表(而不是宏)定义别名? ?

Is there a more preferable, cleaner way to define an alias for this list of types other than a macro?

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

Note: by macro I am referring to defining defining and using this instead of the actual list:

#define VISITABLE_TYPES IntegerElement, StringElement, BoxElement, ImageElement
// Add more types here


推荐答案

std :: tuple 使用是您的朋友。

如果您以此方式定义 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));
        }
};

并通过使用添加

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

您的示例已修改

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

---编辑---

我尝试回答您的评论问题

I try to respond to your comment-questions


为什么模板类Visitable ;在定义实际模板之前需要

why was the template class Visitable; needed before defining the actual template?

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

I don't know if it's possible to do this in a simpler way but... it's because we need "extract" the types from a std::tuple. So you need a general definition (template <typename, typename> to be able to receive the std::tuple<something> type and you need a specialization so you can extract the someting types.


也可以通过
定义一个附加模板,将std :: tuple作为模板
参数,对Visitor模板进行同样巧妙的处理,也可以将其添加到答案中吗?

the same neat trick can be also done for the Visitor template by defining an additional template that takes a std::tuple as template parameter. Can you add this to your answer as well, please?

是的,有可能。

但是您必须修改 VisitableInterface RenderEngine 也是如此。

But you have to modify VisitableInterface and RenderEngine too.

稍作改动改进(IMHO);仅用于定义 RenderEngine tupleT

A big change for a little improvement (IMHO); just for use tupleT defining RenderEngine.

无论如何,您的示例成为

Anyway, your example become

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

这篇关于为类型列表创建别名并将其作为模板参数传递的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆