通过其接口访问可变参数模板结构

Access variadic template structure by its interface

存在 classes 的层次结构:

template<typename T>
class FeatureInterface1 {
public:
    void f1( void ) { static_cast<T&>(*this)._f1(); }
}

class Feature1 : public FeatureInterface1<Feature1> {
    /* Allow interface class to access private elements */
    friend class FeatureInterface<Feature1>;
private:
    void _f1(void) { /* Do something there */ }
}

template<typename T>
class FeatureInterface2 {
public:
    void f2( void ) { static_cast<T&>(*this)._f2(); }
}

class Feature2 : public FeatureInterface2<Feature2> {
    /* Allow interface class to access private elements */
    friend class FeatureInterface<Feature2>;
private:
    void _f2(void) { /* Do something there */ }
}

然后就是一个可变数据class:

template<typename... FEATURES> class Device {};

template<typename FEATURE, typename... OTHERS>
class Device<FEATURE, OTHERS...> : public Device<OTHERS...> {
public:
    /* Contructor */
    Device(FEATURE feature, OTHERS... others)
        : Device<OTHERS...>(others...),
          m_feature( feature ) {
    }
private:
    FEATURE m_feature;
};

最后是在编译时制作的全功能对象:

Device<Feature1, Feature2> device;

任务是设计一个 get<>() 函数,该函数 returns 使用它的接口指向特定对象的指针。 示例用法:

FeatureInterface1<Feature1>* ptr_f = get<FeatureInterface1<Feature1>>(device);

换句话说,类似于 get<0>get<1> ... std::tuple 的访问器,但接口是 class 定义的,而不是索引定义的。

我的想法是将 std::enable_ifstd::is_base_of 结合使用 ...

灵感来自https://eli.thegreenplace.net/2014/variadic-templates-in-c/

任何愿意帮助我的人我都会很高兴。提前致谢!

这其实很简单 if constexpr:

template<typename FEATURE, typename... OTHERS>
class Device<FEATURE, OTHERS...> : public Device<OTHERS...> {
public:
    ...
    template <typename FEATURE_INTERFACE>
    FEATURE_INTERFACE& get()
    {
        if constexpr (std::is_base_of_v<FEATURE_INTERFACE, FEATURE>)
            return m_feature;
        else
            return Device<OTHERS...>::template get<FEATURE_INTERFACE>();
    }
    ...
};

请注意,如果 Device 不支持请求的接口,您将收到编译错误。但是,如果你想要一个 nullptr 而不是那么难,或者对空 Device:

进行额外的专业化
template<>
class Device<> {
public:
    template <typename FEATURE_INTERFACE>
    FEATURE_INTERFACE* get()
    {
        return nullptr;
    }
};

然后只需将主要实现更改为 return 指针:

template <typename FEATURE_INTERFACE>
FEATURE_INTERFACE* get()
{
    if constexpr (std::is_base_of_v<FEATURE_INTERFACE, FEATURE>)
        return &m_feature;
    else
        return Device<OTHERS...>::template get<FEATURE_INTERFACE>();
}

我使用成员函数而不是非成员函数,因为在我看来,这样实现起来更简单,而且我个人也不喜欢那些非成员朋友 getters :)。此外,正如 Red.Wave 在评论中提到的,使用成员 getter 很容易使非成员成为成员:

template <typename FEATURE_INTERFACE, typename... FEATURES>
FEATURE_INTERFACE* get(Device<FEATURES...>& device)
{
    return device.template get<FEATURE_INTERFACE>();
}

为了完整性,您可能还想向所有这些 getter 添加常量重载。

template<typename FEATURE, typename... OTHERS>
class Device<FEATURE, OTHERS...> : public Device<OTHERS...> {
public:
  /* Contructor */
  Device(FEATURE feature, OTHERS... others)
    : Device<OTHERS...>(others...),
      m_feature( feature ) {
  }
  template<class T>
  friend std::enable_if_t<std::is_base_of<T, FEATURE>{}, T&>
  get( Device& d ){ return d.m_feature; }
  template<class T>
  friend std::enable_if_t<std::is_base_of<T, FEATURE>{}, T const&>
  get( Device const& d ){ return d.m_feature; }

应该这样做。

您可能需要一些 template<class T,class U> decltype(auto) get(U&u, ...){ return get<T>(u); } 来启用 ADL(但我不喜欢给出的错误消息),或者一个返回到 get_impl 或其他东西的 get-template。