【问题标题】:Generic visitor base class template in C++ - overload issueC++ 中的通用访问者基类模板 - 重载问题
【发布时间】:2012-02-19 21:41:51
【问题描述】:

我认为编写一个通用的访问者基类模板将是一个简单的练习。目标是能写

typedef visitor<some_base, some_derived1, some_derived2> my_visitor;

...然后让 my_visitor 成为在功能上等同于

的类型
struct my_visitor {
    virtual void visit(some_base&) {}
    virtual void visit(some_derived1&) {}
    virtual void visit(some_derived2&) {}
};

我可以继承该类型层次结构的实际有用的派生访问者类,根据需要覆盖不同的 visit() 版本。我希望它适用于具有任何继承关系的任意数量的类型,并且我不想使用任何使用 type_info 比较重新实现虚函数的技巧。这是我想出的:

#include <cstdlib>
#include <iostream>
#include <vector>


/** This is the generic part that would go in a visitor.hpp header. */
template <typename T> struct visitor_base {
    virtual void visit(T&) {};
};

template <typename... T> struct visitor : visitor_base<T>... {};


/** This is the part that is specific to a certain type hierarchy. */
struct base;
struct derived1;
struct derived2;

typedef visitor<base, derived1, derived2> my_visitor;


/** This is the type hierarchy. */
struct base {
    virtual void accept(my_visitor& v) { v.visit(*this); }
};

struct derived1 : base {
    derived1() : i(42) {}
    virtual void accept(my_visitor& v) { v.visit(*this); }
    int i;
};

struct derived2 : base {
    derived2() : f(2.79) {}
    virtual void accept(my_visitor& v) { v.visit(*this); }
    float f;
};


/** These are the algorithms. */
struct print_visitor : my_visitor {
    void visit(base&) { std::cout<<"that was a base."<<std::endl; }
    void visit(derived1& d) { std::cout<<"that was "<<d.i<<std::endl; }
    void visit(derived2& d) { std::cout<<"that was "<<d.f<<std::endl; }
};

struct randomise_visitor : my_visitor {
    void visit(derived1& d) { d.i = std::rand(); }
    void visit(derived2& d) { d.f = std::rand() / float(RAND_MAX); }
};


int main() {
    std::vector<base*> objects { new base, new derived1, new derived2,
                                 new derived2, new base };

    print_visitor p;
    randomise_visitor r;

    for (auto& o : objects) o->accept(p);
    for (auto& o : objects) o->accept(r);
    for (auto& o : objects) o->accept(p);
}

问题是这不能编译。海合会说

silly_visitor.cpp: In member function ‘virtual void base::accept(my_visitor&)’:
silly_visitor.cpp:24:42: error: request for member ‘visit’ is ambiguous
silly_visitor.cpp:8:16: error: candidates are: void visitor_base<T>::visit(T&) [with T = derived2]
silly_visitor.cpp:8:16: error:                 void visitor_base<T>::visit(T&) [with T = derived1]
silly_visitor.cpp:8:16: error:                 void visitor_base<T>::visit(T&) [with T = base]

基本上,问题在于,由于不同的 visit() 成员函数在不同的类中声明,它们不被视为重载决议的候选者,而只是模棱两可的成员访问。强制编译器考虑继承函数进行重载解析的正常技巧是在派生类中使用“using”语句重新声明它们,但在这种情况下这是不可行的,因为它会破坏它的整个通用方面。

所以,显然这并不像我想象的那么容易。有什么想法吗?

【问题讨论】:

标签: c++ templates inheritance c++11 visitor-pattern


【解决方案1】:

编译器不知道要调用哪个基类'visit 函数。见this question of mine。因此,正如您所说的那样,您需要使用 using 声明使函数在 visitor 类中可用。遗憾的是,您不能只使用using visitor_base&lt;T&gt;::visit...;,因为这不是一个有效的模式。您必须从一个又一个基递归地继承,并且每次都将基类visits 带入派生类的范围:

template <typename T>
struct visitor_base {
    virtual void visit(T&) {};
};

template <typename Head, typename... Tail>
struct recursive_visitor_base
  : visitor_base<Head>
  , recursive_visitor_base<Tail...>
{
  using visitor_base<Head>::visit;
  using recursive_visitor_base<Tail...>::visit;
};

template<class T>
struct recursive_visitor_base<T>
  : visitor_base<T>
{
  using visitor_base<T>::visit;
};

template <typename... T>
struct visitor
  : recursive_visitor_base<T...>
{
  using recursive_visitor_base<T...>::visit;
};

Live example on Ideone(我不得不稍微调整部分规范,因为 GCC 4.5.1 在那部分有点错误。Clang 可以很好地编译这个答案中显示的代码)。输出:

that was a base.
that was 42
that was 2.79
that was 2.79
that was a base.
=================
that was a base.
that was 1804289383
that was 8.46931e+08
that was 1.68169e+09
that was a base.

【讨论】:

  • 不错!我有 GCC 4.5.3,它有关于使用扩展参数包作为模板参数的错误,所以我决定不使用递归方法,然后当我遇到这个重载问题时,我忘记了它。
  • 注意 C++17 允许带有可变参数包的 using 声明:参见 en.cppreference.com/w/cpp/language/…
猜你喜欢
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-04
相关资源
最近更新 更多