为类型列表创建别名并将其作为模板参数传递 [英] Create alias for a list of types and passing it as a template parameter
问题描述
我正在使用可变参数模板来实现访客模式:
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屋!