2016-08-31 12 views
7

Tôi đang sử dụng mẫu variadic để thực hiện mẫu người truy cập:Tạo bí danh cho một danh sách các loại và đi qua nó như một tham số mẫu

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

Giả sử sẽ có nhiều lớp học mà có thể đến thăm, bạn kết thúc với một danh sách các loại rất dài khi kế thừa từ các mẫu VisitableVisitor. Ngoài ra, nếu bạn muốn thêm LinkElement vào các loại có thể truy cập được chấp nhận bởi loại khách truy cập này, bạn phải thêm nó ở mọi nơi.

Vì cùng một danh sách các loại được sử dụng khi kế thừa từ VisitorVisitable (ngoại trừ trường hợp này có loại bổ sung, loại của lớp kế thừa từ đó), tôi muốn thực hiện một giải pháp thanh lịch hơn .

Có cách nào thích hợp hơn, sạch hơn để xác định bí danh cho danh sách loại này ngoài macro không?

Lưu ý: do vĩ mô tôi đề cập đến quy định việc xác định và sử dụng này thay vì danh sách thực tế:

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

lý do xuống bầu cử là gì? – Nikopol

+0

Tôi giả sử mã ví dụ sẽ làm cho câu hỏi tốt hơn một chút. Đôi khi sẽ dễ dàng hơn khi xem mã được đề cập. Ngoài ra nếu mã sẽ có thể biên dịch này cũng sẽ được tốt đẹp vì vậy chúng tôi có thể kiểm tra lại đối với một trình biên dịch đầu tiên mà không cần phải mã một cái gì đó mà có thể là cách bạn dự định nó. – Hayt

+0

Okey, tôi sẽ thêm một số mã tối thiểu để làm rõ câu hỏi. Cảm ơn vì bạn đã phản hồi! :) – Nikopol

Trả lời

1

std::tupleusing là bạn bè.

Nếu bạn xác định Visitable theo cách này

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

và thêm, qua using, một cái gì đó thay thế ý tưởng vĩ mô

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

định nghĩa của các yếu tố của bạn trở nên đơn giản

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

Ví dụ của bạn đã sửa đổi

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

--- EDIT ---

tôi cố gắng để đáp ứng với comment-câu hỏi của bạn

tại sao là lớp mẫu có thể đến thăm; cần trước khi xác định mẫu thực tế?

Tôi không biết nếu nó có thể làm điều này một cách đơn giản hơn nhưng ... đó là bởi vì chúng ta cần "trích xuất" các loại từ std::tuple. Vì vậy, bạn cần có một định nghĩa chung (template <typename, typename> để có thể nhận được các loại std::tuple<something> và bạn cần một chuyên môn hóa, do đó bạn có thể trích xuất các someting loại.

lừa gọn gàng cùng cũng có thể được thực hiện cho mẫu của khách bởi định một mẫu bổ sung mà phải mất một std :: tuple như mẫu tham số. bạn có thể thêm video này vào câu trả lời của bạn là tốt, xin vui lòng?

có, đó là có thể.

Nhưng bạn phải modif y VisitableInterfaceRenderEngine cũng vậy.

Một thay đổi lớn cho một cải tiến nhỏ (IMHO); chỉ để sử dụng tupleT xác định RenderEngine.

Dù sao, ví dụ của bạn trở nên

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

Thật tuyệt vời! Tôi có một câu hỏi mặc dù, tại sao là 'template class Visitable;' cần thiết trước khi xác định các mẫu thực tế? – Nikopol

+0

Nhân tiện, thủ thuật gọn gàng cũng có thể được thực hiện cho mẫu Visitor bằng cách định nghĩa một mẫu bổ sung có tham số '' std :: tuple'' làm tham số mẫu. Bạn cũng có thể thêm câu trả lời này vào câu trả lời của mình được không? – Nikopol

+0

@Nikopol - đã sửa đổi câu trả lời của tôi khi cố gắng trả lời các câu hỏi nhận xét của bạn. – max66

Các vấn đề liên quan