启用 if/else 类成员模板实例化

Enable if/else class member template instantiation

本文关键字:实例化 成员 if else 启用      更新时间:2023-10-16

谁能告诉我如何基于预定义基集中的不同派生类启用 if/else 类成员模板?让我使用以下示例:

enum class Type {
  TYPEA,
  TYPEB
};
// Predefined in libraries.
class BaseA {...};
class BaseB {...};
class Foo {
  template <typename Derived, Type type>
  void foo();
};
// User-derived
class DerivedA : public BaseA {};
class DerivedB : public BaseB {};

通常我们需要两个模板类型名来调用成员 foo。

Foo obj;
obj.foo<DerivedA, Type::TypeA>()
obj.foo<DerivedB, Type::TypeB>();

然而,这种原生方法似乎很长,因为第二个模板参数Type::TypeAType::TypeB显然可以通过编译器通过第一个参数DerivedADerivedB推导出来,如果它们正确地从预定义的基派生。我注意到 c++11 提供了is_base_of模板,但我不确定如何在我的情况下使用它。更具体地说,以下是预期的解决方案:

obj.foo<DerivedA>();  // Automatically deduce type = Type::TypeA
obj.foo<DerivedB>();  // Automatically deduce type = Type::TypeB

如果编译无法从第一个类型名推断出Type,它应该返回到正常的声明obj.foo<MyClass, MyType>,其中MyType要么Type::TypeA要么Type::TypeB

听起来你只需要一个默认的模板参数:

class Foo {
  template <typename Derived, Type type = get_type_from<Derived>::value>
  void foo();
};

其中get_type_from<>是一个元函数,稍后将根据您实际计算Type的方式填充。

template<Type t>
using etype_tag = std::integral_constant<Type, t>;
template<class T>
struct tag_t {
  using type=T;
  template<class D,
    std::enable_if_t<std::is_base_of<T, D>::value, int>* =nullptr
  >
  constexpr tag_t( tag_t<D> ) {}
  constexpr tag_t() = default;
  constexpr tag_t(tag_t const&) = default;
};
template<class T>
constexpr tag_t<T> tag{};

constexpr etype_tag<Type::TYPEA> get_etype( tag_t<BaseA> ) { return {}; }
constexpr etype_tag<Type::TYPEB> get_etype( tag_t<BaseB> ) { return {}; }
template<class T>
constexpr decltype( get_etype( tag<T> ) ) etype{};

现在etype<Bob>是您想要的编译时常量整数常量。

class Foo {
  template <typename Derived, Type type=etype<Derived>>
  void foo();
};

使第二个参数(通常)是多余的。

您可以在声明 etype 的命名空间中,或在 tag_t 的命名空间中,或者在要扩展get_etype要使用的类型的命名空间中使用更多重载来扩展get_etypeetype将自动获得支持(假设它在扩展可见的上下文中使用): 如果满足该要求,您的程序格式不正确)。

现场示例