首页 > 解决方案 > 通过其接口访问可变参数模板结构

问题描述

有一个类的层次结构:

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 */ }
}

然后是一个可变参数数据类:

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<>()函数,该函数使用其接口返回指向特定对象的指针。示例用法:

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

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

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

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

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

标签: c++c++17variadic-templates

解决方案


这实际上非常简单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不支持请求的接口,您将收到编译错误。但是,如果您想要 a ,那么nullptr使用 empty 的额外专业化也不是那么难Device

template<>
class Device<> {
public:
    template <typename FEATURE_INTERFACE>
    FEATURE_INTERFACE* get()
    {
        return nullptr;
    }
};

然后只需更改主要实现以返回一个指针:

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>();
}

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

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

您可能还想为所有这些 getter 添加 const 重载以确保完整性。


推荐阅读