【问题标题】:Nested Conceptual Polymorphic Templates嵌套的概念多态模板
【发布时间】:2021-09-21 05:33:01
【问题描述】:

假设我有编译时多态继承结构:

enum class Enum1 {
    Undefined = 0;
    /* ... */
};
enum class Enum2 {
    Undefined = 0;
    /* ... */
};

template<Enum1 A, Enum2 B>
struct Base {
    int state;
};

struct Derived : public Base <Enum1::Undefined, Enum2::Undefined>> {
    int statederived;
};

有没有办法做类似的事情:

template<Base<Enum1, Enum2> DerivedTemplate>
using Function = std::function<void (DerivedTemplate&)>;

完成:

Function<Derived> & function;

然后使用元编程基于这些派生类型创建一个类?

我可以为这些派生类型中的每一个创建一个类,但我想避免这种情况,因为我有大约 50 个。

基本上,我想避免不同派生类型与相同模板参数的冲突,同时仍然强制执行概念约束。

【问题讨论】:

  • 澄清一下,您是否要求确保Derived 必须继承自Base&lt;Enum1, Enum2&gt;any 实例化的约束?或者您是否试图将其限制为 特定 实例化,例如 Base&lt;Enum1::Foo, Enum2::Bar&gt;
  • 第一个,我想。但是让我明确说明:在我的实现中,实际上每个都存在 Enum1::Sometype、Enum2::Sometype 的一种派生类型,但从技术上讲,情况并非如此。我想做的是为每个特定的潜在派生类型声明这个函数模板,所有这些都在一个语句中。

标签: c++ templates nested metaprogramming c++20


【解决方案1】:
struct Derived : public Base <Enum1::Undefined, Enum2::Undefined>> {
  int statederived;
};

这会创建从DerivedBase&lt;Enum1::Undefined, Enum2::Undefined&gt; 的映射,但不会创建相反的映射。

现在你可以这样做了

template<Enum e1, Enum e2, class D>
struct DerivedExtra:Base<e1, e2> {
  // can static cast to D to get Derived stuff
};

template<Enum1 e1, Enum2 e2>
struct Derived:
  DerivedExtra<e1, e2, Derived<e1, e2>>
{
  constexpr auto E1 = e1;
  constexpr auto E2 = e2;

  // common codes goes here
};

通过专门化DerivedExtra 编写任何自定义派生状态:

template<class D>
struct DerivedExtra<
  Derived<Enum1::Undefined, Enum2::Undefined>,
  D 
>:
  Base<Enum1::Undefined, Enum2::Undefined>
{
  int statederived;
};

现在我们可以获取Enum1Enum2 状态的列表并制作笛卡尔积。

template<auto x>
using constant_t = std::integral_constant<std::decay_t<decltype(x)>, x>;
template<auto x>
constexpr constant_t<x> constant = {};

constexpr auto enum1s = std::make_tuple(
  constant<Enum1::Undefined>,
  constant<Enum1::Defined>
);
constexpr auto enum2s = std::make_tuple(
  constant<Enum2::Undefined>,
  constant<Enum2::Defined>
);

constexpr auto e1xe2 = std::apply( [](auto...e1s) {
  return std::tuple_cat(
    std::apply([e1=e1s](auto...e2s) {
      return std::make_tuple(
        std::make_tuple(
          e1,
          e2s
        )...
      );
    }, enum2s )...
  );
}, enum1s );

Live example.

例如,使用e1xe2s,您可以创建std::tuple&lt;std::function&lt;void(Derived&lt;a, b&gt;)...&gt; 或其变体。

【讨论】:

  • 感谢您出色的解释和回答。
【解决方案2】:

如果您想在Function 上添加一个约束以让DerivedTemplate 派生自Base&lt;someEnum1, someEnum2&gt;,您可以检查DeriveTemplate&amp;&amp; 是否可以使用模板参数someEnum1 和@987654328 转换为Base&lt;someEnum1, someEnum2&gt;&amp;&amp; @:

template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2>&);
template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2> const&);
template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2>&&);
template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2> const&&);

template<typename T>
std::int8_t helper(...);
template<typename T>
std::int32_t helper(decltype(test(std::declval<T&&>()))*);

template<typename D>
inline constexpr bool is_base_of_Base_with_some_Enum = sizeof(helper<D>(nullptr)) == sizeof(std::int32_t);

然后您可以将类型别名Function 更改为:

template<typename D>
struct FunctionImpl : std::enable_if<is_base_of_Base_with_some_Enum<D>, std::function<void(D&)>>{};

template<typename Derive>
using Function = typename FunctionImpl<Derive>::type;

Example.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-14
    • 1970-01-01
    相关资源
    最近更新 更多