我需要填写一些模板魔术才能使以下代码段起作用。
问题是我希望能够使用接受两个参数的命名静态方法为profile = profile.decode('utf-8')
定义一个访问者类。如何填写std::variant
进行派遣工作?
Applicator::apply()
答案 0 :(得分:7)
另一种解决方案:
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);
}
};
答案 1 :(得分:5)
使用现在非常普遍的overloaded
类模板技巧(以及Maxim的技巧来根据其const
的{{1}}的性质对lambda进行排序)来创建一个具有SFINAE功能的函子可以为您寻找的逻辑建模:
operator()
请注意,这是我在Wandbox上测试过的所有Clang版本的ICE,但我没有找到解决方法。完美的转发留给读者练习:)
答案 2 :(得分:3)
嗯,std::is_invocable_r
看起来像是首选的工具。
不幸的是,您将必须获得正确的重载类型,这将完全无法达到目的。
相反,请退后一步,使用库基础知识TS v2 中的std::is_detected
或等效版本以及模板:
template <class... Xs>
using can_Visitor_apply = decltype(Visitor::apply(std::declval<Xs>()...));
if constexpr(std::is_detected_convertible<State, can_Visitor_apply, State&, Event&>())
return Visitor::apply(s, e);
优点是您有一个编译时常数,可以挂断任意决定。缺点是(还)没有一个函数,您只需简单地调用它就可以了。
答案 3 :(得分:3)
如果访问者始终可以申请,那么代码可以很简单
return std::visit([&](auto e) { return Visitor::apply(s, e); }, e);
但是,由于Visitor不能总是适用,因此我们需要使用SFINAE,这需要一组重载的函数模板。功能模板可以这样定义:
template<class EventType>
static auto applyHelper(State s, EventType e, int)
-> decltype(Visitor::apply(s, e)) // only enabled if Visitor::apply(s, e) is a valid expression
{
return Visitor::apply(s, e);
}
template<class EventType>
static State applyHelper(State s, EventType e, long) // long gives a lower precedence
// in overload resolution when argument is 0
{
return s;
}
然后可以实现Applicator::apply
static State apply(State s, Event e) {
return std::visit([&](auto e) { return applyHelper(s, e, 0); }, e);
}