使用 std::variant 的静态多态性
Static polymorphism using std::variant
我正在尝试使用 std::variant 实现静态多态性之类的东西。我想使用 VARIANT_METHOD 或 VARIANT_METHOD_CONST 声明方法,应该采用 return 类型、方法名称、参数和限定符。
#include <variant>
#define VARIANT_METHOD(retType, name, ...) \
template <typename... Args> retType name (Args&&... args) __VA_ARGS__ { \
return std::visit([...args = std::forward<Args>(args)] <typename T>(T& self) -> retType { \
return self.name(args...); \
}, static_cast<variant&>(*this)); \
}
#define VARIANT_METHOD_CONST(retType, name, ...) template <typename... Args> \
retType name (Args&&... args) __VA_ARGS__ { \
return std::visit([...args = std::forward<Args>(args)]<typename T>(const T& self) -> retType { \
return self.name(args...); \
}, static_cast<const variant&>(*this)); \
}
#define VARIANT_FIELD(name) \
decltype(auto) name() noexcept { \
return std::visit([](auto& self) -> decltype(auto) { \
return self.name; \
}, static_cast<variant&>(*this)); \
} \
decltype(auto) name() const noexcept { \
return std::visit([](const auto& self) -> decltype(auto) { \
return self.name; \
}, static_cast<const variant&>(*this)); \
}
struct A {
int field;
int field2;
int func(int a) const noexcept {
return a + field;
}
int func(int a, int b) const noexcept {
return a * a;
}
};
struct B {
int field2;
int func(int a) const noexcept {
return a * a;
}
int func(int a, int b) const noexcept {
return a * a;
}
};
struct C : protected std::variant<A, B> {
using variant::variant;
VARIANT_FIELD(field2);
VARIANT_METHOD_CONST(int, func, const noexcept); // (1)
};
int main() {
std::vector<C> vec;
vec.emplace_back(A{.field = 0, .field2 = 1});
vec.emplace_back(B{.field2 = 3});
for (auto& c : vec) {
c.func(10);
}
}
我无法声明两个名称相同但参数不同的方法。我想写这样的东西:
VARIANT_METHOD_CONST(int, func, (int a), const noexcept);
VARIANT_METHOD_CONST(int, func, (int a, int b), const noexcept);
我所知道的最干净的方法是滥用 operator->*
并制作多态方法指针。您可以获得相同的语法(除了 ->*
而不是 .
),没有宏。
template<class F>
struct poly_member {
F f;
friend decltype(auto) operator->*( auto&& t, poly_member const& self) {
return self.f(decltype(t)(t));
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...>& var, poly_member const& self ) {
return std::visit( self.f, var );
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...>&& var, poly_member const& self ) {
return std::visit( self.f, var );
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...> const& var, poly_member const& self ) {
return std::visit( self.f, var );
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...> const&& var, poly_member const& self ) {
return std::visit( self.f, var );
}
};
template<class F>
poly_member(F)->poly_member<F>;
template<class F>
struct poly_method {
F f;
auto operator()(auto&&...args)const {
return poly_member{[&](auto&& t)->decltype(auto){
return f( decltype(t)(t), decltype(args)(args)... );
}};
}
friend auto operator->*( auto&& t, poly_method const& self) {
return [&](auto&&...args)->decltype(auto){
return t->*self.f(decltype(args)(args)...);
};
}
friend auto operator->*( auto* t, poly_method const& self) {
return (*t)->*self;
}
};
template<class F>
poly_method(F)->poly_method<F>;
有点c++20 alphabet soup. Replace the auto
args with template<class T>
etc for a c++14版本。
你最终制作了这样的多边形成员:
constexpr poly_member field { [](auto&& t)->decltype(auto){ return t.field; } };
constexpr poly_member field2 { [](auto&& t)->decltype(auto){ return t.field2; } };
constexpr poly_method func { [](auto&& t, auto&&...args)->decltype(auto){ return t.func(decltype(args)(args)...); } };
您的代码如下所示:
for (auto& c : vec) {
c->*func(10);
}
现在,如果您不喜欢编写那些 constexpr lambda,您可以让它们由宏编写。
那些 poly 方法和成员适用于支持存储在其中的 lambda 的任何 class 的任何实例。成员和方法之间的区别在于方法需要尾随 () 并传入这些参数,而成员则不需要。
例如:
A a;
a->*func(3);
作用于 a
就像它作用于 variant<A, other stuff>
。
如果你真的非常依赖你的语法,我建议你看看 google mock 是如何做到的。
我有时会扩展它并写
template<auto*...methods>
struct poly_any:private std::any {
// code that makes the above alphabet soup look cute
};
并为我存储的 std::any
动态构建 vtables 以访问每个 method
,给你:
using C = poly_any< &field, &field2, &func >;
C c = A{};
std::cout << c->*field;
std::cout << c->*func(2,3);
我们在其中键入擦除任何支持这些方法的内容。
但那是我个人的精神错乱。 (此扩展要求您使用部分签名信息标记 poly members/methods)。
我正在尝试使用 std::variant 实现静态多态性之类的东西。我想使用 VARIANT_METHOD 或 VARIANT_METHOD_CONST 声明方法,应该采用 return 类型、方法名称、参数和限定符。
#include <variant>
#define VARIANT_METHOD(retType, name, ...) \
template <typename... Args> retType name (Args&&... args) __VA_ARGS__ { \
return std::visit([...args = std::forward<Args>(args)] <typename T>(T& self) -> retType { \
return self.name(args...); \
}, static_cast<variant&>(*this)); \
}
#define VARIANT_METHOD_CONST(retType, name, ...) template <typename... Args> \
retType name (Args&&... args) __VA_ARGS__ { \
return std::visit([...args = std::forward<Args>(args)]<typename T>(const T& self) -> retType { \
return self.name(args...); \
}, static_cast<const variant&>(*this)); \
}
#define VARIANT_FIELD(name) \
decltype(auto) name() noexcept { \
return std::visit([](auto& self) -> decltype(auto) { \
return self.name; \
}, static_cast<variant&>(*this)); \
} \
decltype(auto) name() const noexcept { \
return std::visit([](const auto& self) -> decltype(auto) { \
return self.name; \
}, static_cast<const variant&>(*this)); \
}
struct A {
int field;
int field2;
int func(int a) const noexcept {
return a + field;
}
int func(int a, int b) const noexcept {
return a * a;
}
};
struct B {
int field2;
int func(int a) const noexcept {
return a * a;
}
int func(int a, int b) const noexcept {
return a * a;
}
};
struct C : protected std::variant<A, B> {
using variant::variant;
VARIANT_FIELD(field2);
VARIANT_METHOD_CONST(int, func, const noexcept); // (1)
};
int main() {
std::vector<C> vec;
vec.emplace_back(A{.field = 0, .field2 = 1});
vec.emplace_back(B{.field2 = 3});
for (auto& c : vec) {
c.func(10);
}
}
我无法声明两个名称相同但参数不同的方法。我想写这样的东西:
VARIANT_METHOD_CONST(int, func, (int a), const noexcept);
VARIANT_METHOD_CONST(int, func, (int a, int b), const noexcept);
我所知道的最干净的方法是滥用 operator->*
并制作多态方法指针。您可以获得相同的语法(除了 ->*
而不是 .
),没有宏。
template<class F>
struct poly_member {
F f;
friend decltype(auto) operator->*( auto&& t, poly_member const& self) {
return self.f(decltype(t)(t));
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...>& var, poly_member const& self ) {
return std::visit( self.f, var );
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...>&& var, poly_member const& self ) {
return std::visit( self.f, var );
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...> const& var, poly_member const& self ) {
return std::visit( self.f, var );
}
template<class...Ts>
friend decltype(auto) operator->*( std::variant<Ts...> const&& var, poly_member const& self ) {
return std::visit( self.f, var );
}
};
template<class F>
poly_member(F)->poly_member<F>;
template<class F>
struct poly_method {
F f;
auto operator()(auto&&...args)const {
return poly_member{[&](auto&& t)->decltype(auto){
return f( decltype(t)(t), decltype(args)(args)... );
}};
}
friend auto operator->*( auto&& t, poly_method const& self) {
return [&](auto&&...args)->decltype(auto){
return t->*self.f(decltype(args)(args)...);
};
}
friend auto operator->*( auto* t, poly_method const& self) {
return (*t)->*self;
}
};
template<class F>
poly_method(F)->poly_method<F>;
有点c++20 alphabet soup. Replace the auto
args with template<class T>
etc for a c++14版本。
你最终制作了这样的多边形成员:
constexpr poly_member field { [](auto&& t)->decltype(auto){ return t.field; } };
constexpr poly_member field2 { [](auto&& t)->decltype(auto){ return t.field2; } };
constexpr poly_method func { [](auto&& t, auto&&...args)->decltype(auto){ return t.func(decltype(args)(args)...); } };
您的代码如下所示:
for (auto& c : vec) {
c->*func(10);
}
现在,如果您不喜欢编写那些 constexpr lambda,您可以让它们由宏编写。
那些 poly 方法和成员适用于支持存储在其中的 lambda 的任何 class 的任何实例。成员和方法之间的区别在于方法需要尾随 () 并传入这些参数,而成员则不需要。
例如:
A a;
a->*func(3);
作用于 a
就像它作用于 variant<A, other stuff>
。
如果你真的非常依赖你的语法,我建议你看看 google mock 是如何做到的。
我有时会扩展它并写
template<auto*...methods>
struct poly_any:private std::any {
// code that makes the above alphabet soup look cute
};
并为我存储的 std::any
动态构建 vtables 以访问每个 method
,给你:
using C = poly_any< &field, &field2, &func >;
C c = A{};
std::cout << c->*field;
std::cout << c->*func(2,3);
我们在其中键入擦除任何支持这些方法的内容。
但那是我个人的精神错乱。 (此扩展要求您使用部分签名信息标记 poly members/methods)。