-1

我正在尝试使用 std::variant 实现类似静态多态的东西。我想声明使用 VARIANT_METHOD 或 VARIANT_METHOD_CONST 的方法,这些方法应该采用返回类型、方法名称、参数和限定符。

#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);
4

1 回答 1

1

我所知道的最干净的方法是滥用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>;

一点字母汤。对于版本,将autoargs替换为etc。template<class T>

你最终会像这样制作 poly 成员:

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 的任何类的任何实例。成员和方法之间的区别在于方法需要尾随 () 并传入这些参数,而成员则不需要。

例如:

A a;
a->*func(3);

作用于 aa就像作用于 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
};

并为 I 存储动态构建 vtablestd::any以访问 each method,从而为您提供:

using C = poly_any< &field, &field2, &func >;
C c = A{};
std::cout << c->*field;
std::cout << c->*func(2,3);

我们在其中键入擦除任何支持这些方法的内容。

但那是我个人的精神错乱。(此扩展要求您使用部分签名信息标记 poly 成员/方法)。

于 2021-04-04T18:28:52.690 回答