有一位经典访客:
struct Visitor
{
virtual ~Visitor() = default;
virtual void visit(X& x) {}
virtual void visit(Y& y) {}
virtual void visit(Z& z) {}
};
和
struct Object
{
virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
X,Y,Z派生自Object。
我想让访客正确地使用lambdas,就像这样:
auto visitor = make_visitor<Visitor>
(
[](X& x) {do operations on x},
//for Y do not want to do anything. default implementation is suitable.
[](Z& z) {do operations on z}
);
object.accept(visitor);
有没有想法如何实现make_visitor?
(我看了https://accu.org/index.php/journals/2160并且它很棒 - 我只想要一个更短更方便的make_visitor形式)
答案 0 :(得分:1)
几年前我想做同样的事情,我想出了这个:
template<typename ResultType, typename ... Callables>
class visitor;
template<typename ResultType, typename Callable, typename ... Callables>
class visitor<ResultType, Callable, Callables...>
: public Callable, public visitor<ResultType, Callables...>::type
{
public:
using type = visitor;
public:
visitor(Callable callable, Callables... callables)
:
Callable(callable), visitor<ResultType, Callables...>::type(callables...)
{
}
public:
using Callable::operator();
using visitor<ResultType, Callables...>::type::operator();
}; // class visitor
template <typename ResultType, typename Callable>
class visitor<ResultType, Callable>
: public Callable, public boost::static_visitor<ResultType>
{
public:
using type = visitor;
public:
visitor(Callable callable)
:
Callable(callable)
{
}
public:
using Callable::operator();
}; // calss visitor
template<typename ResultType = void, typename ... Callables>
typename visitor<ResultType, Callables...>::type
make_visitor(Callables... callables)
{
return (typename visitor<ResultType, Callables...>::type(callables...));
}
不是调用名为visit
的方法,而是使用调用运算符operator()
,但可以通过添加只调用callables的visit
方法进行修改。
希望在实现方面明白这一点很清楚,简而言之,它是一个继承自所有不同lambdas的类,因此它将所有主要函数组合在一个类中。
当时主要受到这个答案的启发:https://stackoverflow.com/a/18731900/1147772。
答案 1 :(得分:1)
我将在C ++ 17中这样做。
首先,我们从覆盖的想法开始:
template<class T>
struct override_helper { using type=T; };
template<class T>
using override_helper_t = typename override_helper<T>::type;
template<class R, class...Args>
struct override_helper<R(*)(Args...)> {
struct type {
R(*f)(Args...);
R operator()(Args...args)const { return f(std::forward<Args>(args)...); }
type(R(*in)(Args...)):f(in) {}
};
};
template<class R, class...Args>
struct override_helper<R(&)(Args...)>:override_helper<R(*)(Args...)> {
using override_helper<R(*)(Args...)>::override_helper;
};
template<class...Fs>
struct override:override_helper_t<Fs>... {
using override_helper_t<Fs>::operator()...;
override(Fs...fs):override_helper_t<Fs>(std::move(fs))... {}
};
现在我们可以这样做:
auto f = override(
[](X& x) {do operations on x},
[](Z& z) {do operations on z},
[](auto&) {default operation goes here}
);
和f
现在是一个接受X,Y和Z的变体式访问者。
然后我们重写Object。它暴露了variant
,或者我们伪造它。
template<class D, class Sig>
struct function_invoker;
template<class D, class R, class...Args>
struct function_invoker<D, R(Args...)> {
R operator()(Args...args)const {
return f(
static_cast<D const*>(this)->get(),
std::forward<Args>(args)...
);
}
template<class F>
function_invoker( F&& fin ):
f([](void* ptr, Args&&...args)->R{
auto* pf = static_cast<std::remove_reference_t<F>*>(ptr);
return (*pf)(std::forward<Args>(args)...);
})
{}
private:
R(*f)(void*, Args&&...) = 0;
};
template<class...Sigs>
struct function_view :
private function_invoker<function_view<Sigs...>, Sigs>...
{
template<class D, class Sig>
friend class function_invoker;
using function_invoker<function_view<Sigs...>, Sigs>::operator()...;
template<class F,
std::enable_if_t< !std::is_same<std::decay_t<F>, function_view>{}, bool> =true
>
function_view( F&& fin ):
function_invoker<function_view<Sigs...>, Sigs>( fin )...,
ptr((void*)std::addressof(fin))
{}
explicit operator bool() const { return ptr; }
private:
void* get() const { return ptr; }
void* ptr = 0;
};
这是一个多重签名可调用函数指针类型擦除。
using Visitor = function_view< void(X&), void(Y&), void(Z&) >;
Visitor
现在是任何可调用的视图类型,可以使用任何X,Y或Z引用调用。
struct Object
{
virtual void accept(Visitor visitor) = 0;
};
template<class D>
struct Object_derived:Object {
virtual void accept(Visitor visitor) final override {
visitor(*static_cast<D*>(this));
}
};
struct X:Object_derived<X> {};
struct Y:Object_derived<Y> {};
struct Z:Object_derived<Z> {};
现在您可以将[](auto&){}
传递给Object::accept
并进行编译。
然后我们挂钩覆盖,我们传入一个可调用的覆盖。
function_view
存储指向覆盖对象的指针和一个指示如何调用每个覆盖的函数指针。
实施accept
时选择了哪一个。
我在这里所做的一切都可以在c++11中完成,但在c++17中要容易得多,所以我在那里作为概念证明。
function_view可能想要SFINAE友好的ctor来检测它的参数是否满足所有签名,但我很懒。
答案 2 :(得分:0)
这是另一种选择。构造一个compound_visitor
,指定默认仿函数以及您要支持的类型:
template<typename T>
struct output_default {
void operator()(T&) {
std::cout << "default";
}
};
typedef compound_visitor<output_default, node1, node2, node3, node4> concrete_visitor;
然后你可以用函数指针,lambdas或std :: function覆盖一些节点类型visit
方法,注意我没有为node4
提供一个函数所以它默认为由output_default<node4>
:
auto v = make_compound_visitor<concrete_visitor>(
[](node1& node) -> void { std::cout << "n1";},
std::function<void(node2&)>([](node2& node) -> void { std::cout << "n2";}),
+[](node3& node) -> void { std::cout << "n3";}
);
完整代码:
#include <iostream>
#include <functional>
template<typename T>
struct arg_type :
public arg_type<decltype(&T::operator())> {};
template<typename T>
struct arg_type<void(*)(T&)> :
public arg_type<void(T&)> {};
template<typename T, typename C>
struct arg_type<void(C::*)(T&) const > :
public arg_type<void(T&)> {};
template<typename T>
struct arg_type<void(T&)> {
typedef T type;
};
template<typename T, template<typename> typename D>
class visitor {
public:
visitor():
f_(D<T>()) {
}
void visit(T& node) {
if(f_) {
f_(node);
}
}
void set(std::function<void(T&)> f) {
f_ = f;
}
private:
std::function<void(T&)> f_;
};
template<template<typename> typename D, typename ...T>
class compound_visitor : public visitor<T, D>... {
public:
template<typename U>
void visit(U& node) {
this->visitor<U, D>::visit(node);
}
template<typename F>
void set(F f) {
this->visitor<typename arg_type<F>::type, D>::set(f);
}
};
template<typename C, typename F>
auto set(C& c, F f) {
c.set(f);
}
template<typename C, typename F, typename ...Fs>
auto set(C& c, F f, Fs... fs) {
set(c, f);
set(c, fs...);
}
template<typename C, typename ...F>
auto make_compound_visitor(F... f) {
C c;
set(c, f...);
return c;
}
template<typename T>
struct output_default {
void operator()(T&) {
std::cout << "default";
}
};
// usage
class node1;
class node2;
class node3;
class node4;
typedef compound_visitor<output_default, node1, node2, node3, node4> concrete_visitor;
class node1 {
public:
void accept(concrete_visitor& v) {
v.visit(*this);
}
};
class node2 {
public:
void accept(concrete_visitor& v) {
v.visit(*this);
}
};
class node3 {
public:
void accept(concrete_visitor& v) {
v.visit(*this);
}
};
class node4 {
public:
void accept(concrete_visitor& v) {
v.visit(*this);
}
};
int main(int argc, char** argv) {
auto v = make_compound_visitor<concrete_visitor>(
[](node1& node) -> void { std::cout << "n1";},
std::function<void(node2&)>([](node2& node) -> void { std::cout << "n2";}),
+[](node3& node) -> void { std::cout << "n3";}
);
node1 n1;
node2 n2;
node3 n3;
node4 n4;
n1.accept(v);
n2.accept(v);
n3.accept(v);
n4.accept(v);
return 0;
}
以上代码输出:
n1n2n3default
我已将此代码放入github。我认为这对某人https://github.com/the4thamigo-uk/inline-visitor
有用答案 3 :(得分:-1)
如果你想要更简单的东西,你可以从这个草图代码中解决问题,(或者你可以使用std :: function而不是原始函数指针,我没有处理默认的实现部分,但这是一个逻辑扩展我想):
#include <iostream>
struct X{};
struct Y{};
struct Z{};
struct Visitor
{
Visitor(void (*xf)(X&), void (*yf)(Y&), void (*zf)(Z&)):
xf_(xf), yf_(yf), zf_(zf) {
}
virtual ~Visitor() = default;
void visit(X& x) {xf_(x);}
void visit(Y& y) {yf_(y);}
void visit(Z& z) {zf_(z);}
private:
void (*xf_)(X& x);
void (*yf_)(Y& x);
void (*zf_)(Z& x);
};
template<typename T>
T make_visitor(void (*xf)(X&),void (*yf)(Y&),void (*zf)(Z&)) {
return T(xf, yf, zf);
}
int main(int argc, char** argv) {
auto visitor = make_visitor<Visitor>
(
[](X& x) {std::cout << "x";},
[](Y& y) {std::cout << "y";},
[](Z& z) {std::cout << "z";}
);
X x;
Y y;
Z z;
visitor.visit(x);
visitor.visit(y);
visitor.visit(z);
return 0;
}