我需要填写一些模板魔术来使以下代码片段起作用.
问题是我希望能够为std::variant接受两个参数的命名静态方法定义一个访问者类.如何填写Applicator::apply()调度工作?
struct EventA {};
struct EventB {};
struct EventC {};
using Event = std::variant<EventA, EventB, EventC>;
struct Visitor {
enum class LastEvent { None, A, B, C };
struct State {
LastEvent last_event = LastEvent::None;
};
static State apply(State s, EventA e) { return State{LastEvent::A}; }
static State apply(State s, EventB e) { return State{LastEvent::B}; }
};
template <typename Visitor> struct Applicator {
static State apply(State s, Event e) {
/*** Start of pseudo code ***/
if (Visitor can apply) {
return Visitor::apply(s, e);
}
/*** End of pseudo code ***/
// Else, don't update state state
return s;
}
};
int main() {
// Handled by visitor
State s1 = Applicator<Visitor>::apply(State{}, EventA{});
assert(s1.last_event == Visitor::LastEvent::A);
// Handled by visitor
State s2 = Applicator<Visitor>::apply(State{}, EventB{});
assert(s2.last_event == Visitor::LastEvent::B);
// NOT handled by visitor
State s3 = Applicator<Visitor>::apply(State{}, EventC{});
assert(s3.last_event == Visitor::LastEvent::None);
}
Run Code Online (Sandbox Code Playgroud)
另一种方案:
using State = Visitor::State;
template<class Visitor>
struct VisitorProxy {
State s;
template<class E>
auto operator()(E const& e) -> decltype(Visitor::apply(s, e)) {
return Visitor::apply(s, e);
}
template<class E>
State operator()(E const&) const {
return s;
}
};
template <typename Visitor> struct Applicator {
static State apply(State s, Event e) {
VisitorProxy<Visitor> p{s};
return std::visit(p, e);
}
};
Run Code Online (Sandbox Code Playgroud)
使用现在很常见的overloaded类模板伎俩(马克西姆的把戏基础上订购lambda表达式const他们的岬operator())来创建一个SFINAE能力的仿造型你lookig的逻辑:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
// ...
template <typename Visitor> struct Applicator {
static typename Visitor::State apply(typename Visitor::State s, Event e) {
return std::visit(overloaded{
[&s](auto e) mutable -> decltype(Visitor::apply(s, e)) { return Visitor::apply(s, e); },
[&s](auto) { return s; }
}, e);
}
};
Run Code Online (Sandbox Code Playgroud)
请注意,这个ICE所有版本的Clang我都在Wandbox上测试过,但我还没有找到解决方法.完美转发留给读者作为练习:)