【问题标题】:Templated visitor that hides overloaded virtual function : SFINAE on using?隐藏重载虚函数的模板化访问者:使用 SFINAE?
【发布时间】:2018-09-02 12:06:20
【问题描述】:

我正在编写一个模板化的访问者(取决于我们要访问的类型):

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

我确实了解问题可能是什么,但如果不在 TypedNodeVisitor&lt;&gt; 的空特化中添加伪造的 apply() 定义,我将无法解决它。

有没有办法在using TypedNodeVisitor&lt;Ts...&gt;::apply 上使用std::enable_if

【问题讨论】:

  • 您需要提供minimal reproducible example
  • 只要我实现了虚拟方法,它就对我有用。 ideone.com/a8U9HG
  • 我会将接口应用从访问的实现中分离出来。你想同时做这两件事有理由吗?
  • @super 我编辑了我的帖子。 vdavid 你只使用一个节点,也许这就是你没有警告的原因。 Yakk 我不明白你的建议是什么。你的意思是做一个界面Applyable和一个界面Visitor?这并不容易,因为您可以添加许多不同的类型 :)
  • @Yakk-AdamNevraumont 好的,我想我明白你的意思了。我需要同时做这两件事,但我认为这是因为我有一些设计问题,所以我正在努力:p

标签: c++ templates c++17 variadic-templates sfinae


【解决方案1】:

有没有办法在 using TypedNodeVisitor&lt;Ts...&gt;::apply 上使用 enable_if ?

据我所知,你不能:你不能模板化那种类型的 using,所以你可以使用 SFINAE 来启用/禁用它。

我认为定义“在TypedNodeVisitor&lt;&gt; 的空特化中应用的虚假定义”没有什么错(恕我直言,这是一个简单而优雅的解决方案)但是......如果你真的想避免它,你可以定义一个TypedNodeVisitor&lt;T0&gt; 特化,而不是TypedNodeVisitor&lt;&gt;,如下

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

这样你也可以避免visit()成员中的if constexpr测试(但你也可以避免它也在TypedNodeVisitor&lt;&gt;中添加一个假的visit()

【讨论】:

  • 好吧,我认为写一个伪造的 apply 定义是个坏主意。顺便说一句,您的解决方案强制为 Ts ... 和 T0 编写此代码。所以我想我会去我的第一个想法:)
  • @AntoineMorrier - “我想我会实现我的第一个想法” 最好的解决方案,恕我直言;我还建议在TypedNodeVisitor&lt;&gt; 中添加一个假的virtual void visit (INode &amp;) {} 以避免if constexpr(但仅用于美学改进)。
  • if constexpr 避免调用一个虚函数。但是,编译器确实必须对其进行优化^^
【解决方案2】:
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 中删除应用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-16
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多