Шаблонный посетитель, который скрывает перегруженную виртуальную функцию: SFINAE при использовании? - PullRequest
0 голосов
/ 02 сентября 2018

Я пишу посетителю по шаблону (в зависимости от типов, которые мы хотим посетить):

#include <iostream>
#include <memory>
#include <vector>
#include <string>

class INode;
class INodeVisitor {
public:
    virtual void visit(INode&) = 0;
    virtual ~INodeVisitor() = default;
};

template<typename ...Ts>
class TypedNodeVisitor;

template<typename T1, typename ...Ts>
class TypedNodeVisitor<T1, Ts...> : public TypedNodeVisitor<Ts...> {
public:
    virtual void visit(INode &v) override {
        if(auto p = dynamic_cast<T1*>(std::addressof(v))) {
            apply(*p);
        }

        if constexpr(sizeof...(Ts) != 0) {
            TypedNodeVisitor<Ts...>::visit(v);
        }
    }

    //using TypedNodeVisitor<Ts...>::apply;

    virtual void apply(T1 &) = 0;
};

template<>
class TypedNodeVisitor<> : public INodeVisitor {};

class INode {
public:
    void accept(INodeVisitor &nv) {
        nv.visit(*this);
    }

    virtual ~INode() = default;
};

class NodeB : public INode {};
class NodeA : public INode {};

class DrawerVisitor : public TypedNodeVisitor<NodeA, NodeB> {
public:
    void apply(NodeA &) override {
        std::cout << "A" << std::endl;
    }

    void apply(NodeB &) override {
        std::cout << "B" << std::endl;
    }
};

int main()
{
    auto nodeA = std::make_shared<NodeA>();
    auto nodeB = std::make_shared<NodeB>();
    DrawerVisitor visitor;
    nodeA->accept(visitor);
    nodeB->accept(visitor);

    return 0;
}

С лязгом я получаю эти предупреждения:

    prog.cc:49:30: note: in instantiation of template class 'TypedNodeVisitor<NodeA, NodeB>' requested here
class DrawerVisitor : public TypedNodeVisitor<NodeA, NodeB> {
                             ^
prog.cc:31:18: note: hidden overloaded virtual function 'TypedNodeVisitor<NodeB>::apply' declared here: type mismatch at 1st parameter ('NodeB &' vs 'NodeA &')
    virtual void apply(T1 &) = 0;

Я понимаю, в чем проблема, но я не могу ее исправить, не добавив поддельное определение apply() в пустую специализацию TypedNodeVisitor<>.

Есть ли способ использовать std::enable_if на using TypedNodeVisitor<Ts...>::apply?

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018
template<class...Ts>
struct IApply{};
template<class T0, class T1, class...Ts>
struct IApply<T0,T1,Ts...>:
  IApply<T0>,IApply<T1,Ts...>
{
  using IApply<T0>::apply;
  using IApply<T1,Ts...>::apply;
};
template<class T0>
struct IApply<T0>{
  virtual void apply(T0&)=0;
};

Далее сделайте это:

template<class Base, typename ...Ts>
class TypedNodeVisitor:public Base{};// 0 case

template<class Base, typename T1, typename ...Ts>
class TypedNodeVisitor<Base, T1, Ts...>: public TypedNodeVisitor<Base, Ts...> {

Теперь мы можем настроить нашу базу.

template<class...Ts>
struct IBase: INodeVisitor, IApply<Ts...>{};

template<class...Ts>
struct ITypeNodeVisitor:TyoedNodeVisitor<IBase<Ts...>,Ts...>{};

class DrawerVisitor : public ITypedNodeVisitor<NodeA, NodeB> {

и готово.

Также удалите приложение из TypedNodeVisitor.

0 голосов
/ 02 сентября 2018

Есть ли способ использовать enable_if при использовании TypedNodeVisitor<Ts...>::apply?

Насколько я знаю, вы не можете: вы не можете шаблонизировать этот тип using, поэтому вы можете использовать SFINAE, чтобы включить / отключить его.

Я не вижу ничего плохого в определении "поддельного определения применения в пустой специализации TypedNodeVisitor<>" (ИМХО, это простое и элегантное решение), но ... если вы действительно хотите избежать этого, вы можете определить TypedNodeVisitor<T0> специализация, вместо TypedNodeVisitor<>, следующим образом

template <typename T0>
class TypedNodeVisitor<T0> : public INodeVisitor
 {
   public: 
    virtual void visit(INode &v) override {
        if(auto p = dynamic_cast<T0*>(std::addressof(v))) {
            apply(*p);
        }
    }

    virtual void apply(T0 &) = 0;
 };

Таким образом, вы также можете избежать теста if constexpr в элементе visit() (но вы также можете избежать добавления фальшивого visit() в TypedNodeVisitor<>)

...