C++中的通用访问者基类模板-重载问题
Generic visitor base class template in C++ - overload issue
我认为编写一个通用的访问者基类模板是一个简单的练习。目标是能够编写
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);
}
问题是它不能编译。GCC称
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"语句重新声明它们,但在这种情况下这是不可行的,因为这会破坏它的整个泛型方面
所以,显然这并不像我想象的那么容易。有什么想法吗?
编译器不知道要调用哪个基类的visit
函数。看看我的这个问题。因此,正如您正确地说的,您需要使用using
声明使函数在visitor
类中可用。遗憾的是,不能只使用using visitor_base<T>::visit...;
,因为这不是一个有效的模式。您必须从一个又一个基类递归继承,每次都将基类visit
s带入派生类的范围:
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编译了这个答案中显示的代码)。输出:
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++中重载布尔运算符的问题
- 在C++中使用重载提取运算符时出现问题
- 重载运算符与添加问题
- 加、乘、除、减复数的问题 C++ - 运算符重载
- 0xC0000005:访问冲突读取位置 0x00000000. 重载 == 运算符的问题
- 关于重载 -> 运算符中 const 关键字的特定位置的问题
- 基于 SFINAE 的特征实现问题与函数模板重载
- C++使用模板参数来解决重载问题
- 为什么C++在自定义结构向量时会出现 ostream 重载问题?
- 链表运算符重载问题
- glUniforms()和[]运算符重载问题
- C++类型重载问题
- C++中的通用访问者基类模板-重载问题
- 64位中指针和int的模板重载问题
- 操作符+重载问题
- 操作符重载问题
- 操作符重载问题
- 操作符重载问题
- c++歧义重载问题
- 运算符重载问题C++