C ++中的通用访问者基类模板 - 重载问题 [英] Generic visitor base class template in C++ - overload issue

查看:136
本文介绍了C ++中的通用访问者基类模板 - 重载问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I thought it would be a simple exercise to write a generic visitor base class template. The goal is to be able to write

typedef visitor<some_base, some_derived1, some_derived2> my_visitor;

...然后my_visitor是功能上等同于

...and then have my_visitor be a type that is functionally equivalent to

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

我可以为该类型层次结构继承实际有用的派生访问类,它覆盖不同的访问)版本。我希望它可以工作任何数量的类型有任何继承关系,我不想使用任何重新实现虚函数使用type_info比较的hack。这是我想出来的:

which i can inherit with actually useful derived visitor classes for that type hierarchy, which override the different visit() versions as needed. I want it to work for any number of types having any inheritance relations, and i don't want to use any hacks that reimplement virtual functions using type_info comparisons. This is what I came up with:

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

问题是这不会编译。 GCC说

The problem is that this does not compile. GCC says

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'语句在派生类中重新声明它们,但是在这种情况下这是不可行的,因为它会破坏它的整个通用方面。

Basically, the problem is that since the different visit() member functions are declared in different classes, they are not seen as candidates for overload resolution but just as ambiguous member access. The normal trick for forcing the compiler to consider inherited functions for overload resolution is to redeclare them in the derived class with 'using' statements, but that is not feasible in this case since it would ruin the whole generic aspect of it.

所以,显然这不像我想的那么容易。任何想法?

So, apparently this was not as easy as I thought. Any ideas?

推荐答案

编译器不知道访问 c $ c>函数调用。请参见我的这个问题。因此,正如你所说,你需要在 visitor 类中使用使用声明来提供这些函数。很遗憾,您不能使用使用visitor_base< T> :: visit ...; ,因为它不是有效的模式。你必须递归地继承一个接一个的基础,并且每次都将基类访问到导出类的范围:

The compiler doesn't know which base-class' visit function to call. See this question of mine. As such, as you correctly said, you need to make the functions available in the visitor class with using declarations. Sadly, you can't just use using visitor_base<T>::visit...;, as that is not a valid pattern. You have to recursively inherit from one base after another and everytime bring the base-class visits into the scope of the derived class:

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

在Ideone上的实例(我不得不调整部分规范有一点,因为GCC 4.5.1是一个有点儿bug在这部分。Clang编译这个答案显示的代码很好)。输出:

Live example on Ideone (I had to tweak the partial spec a bit, since GCC 4.5.1 is a bit buggy in that part. Clang compiles the code shown in this answer just fine). Output:

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.

这篇关于C ++中的通用访问者基类模板 - 重载问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆