创建伪方法指针
这是一个高级示例。
你可以使用变体进行轻量级擦除。
template<class F>
struct pseudo_method {
F f;
// enable C++17 class type deduction:
pseudo_method( F&& fin ):f(std::move(fin)) {}
// Koenig lookup operator->*, as this is a pseudo-method it is appropriate:
template<class Variant> // maybe add SFINAE test that LHS is actually a variant.
friend decltype(auto) operator->*( Variant&& var, pseudo_method const& method ) {
// var->*method returns a lambda that perfect forwards a function call,
// behaving like a method pointer basically:
return [&](auto&&...args)->decltype(auto) {
// use visit to get the type of the variant:
return std::visit(
[&](auto&& self)->decltype(auto) {
// decltype(x)(x) is perfect forwarding in a lambda:
return method.f( decltype(self)(self), decltype(args)(args)... );
},
std::forward<Var>(var)
);
};
}
};
这会创建一个类型,在左侧使用 Variant
重载 operator->*
。
// C++17 class type deduction to find template argument of `print` here.
// a pseudo-method lambda should take `self` as its first argument, then
// the rest of the arguments afterwards, and invoke the action:
pseudo_method print = [](auto&& self, auto&&...args)->decltype(auto) {
return decltype(self)(self).print( decltype(args)(args)... );
};
现在,如果我们有两种类型,每种类型都有 print
方法:
struct A {
void print( std::ostream& os ) const {
os << "A";
}
};
struct B {
void print( std::ostream& os ) const {
os << "B";
}
};
请注意它们是不相关的类型。我们可以:
std::variant<A,B> var = A{};
(var->*print)(std::cout);
它将直接向我们发送调用给 A::print(std::cout)
。如果我们用 B{}
初始化 var
,它将发送到 B::print(std::cout)
。
如果我们创建了一个新类型 C:
struct C {};
然后:
std::variant<A,B,C> var = A{};
(var->*print)(std::cout);
将无法编译,因为没有 C.print(std::cout)
方法。
扩展上述内容将允许检测和使用自由函数 print
s,可能在 print
伪方法中使用 if constexpr
。
活生生的例子 ,当前使用的地方 std::variant
的 boost::variant
。