【问题标题】:variadic templates: how can I check if a specific class is part of the pack, and execute a specific method from that class if the class exists可变参数模板:如何检查特定类是否是包的一部分,如果该类存在则执行该类的特定方法
【发布时间】:2017-01-26 12:05:57
【问题描述】:

考虑以下为BaseSensor 类提供附加功能的混合。

class PeakSensor{ /*...*/ };
class TroughSensor{ /*...*/ };

template<typename EdgeType> //EdgeType can be PeakSensor or TroughtSensor
class EdgeSensor : public EdgeType
{
    public:
        void saveEdges(){}
}

class TrendSensor 
{   
    public:
        void saveTrends(){}
}

template<typename ... SensorType>
class BaseSensor : public SensorType ... //SensorType can be TrendSensor, EdgeSensor or others...
{
    public:
        void saveSensor();
}

在哪里

template<typename ... SensorType>
void BaseSensor<SensorType...>::saveSensor()
{
    this->saveTrends();
    this->saveEdges();
}

和 main.cpp

int main(int , const char **) 
{
    { //this works
        BaseSensor<EdgeSensor<TroughEdge> , TrendSensor> eps;
        eps.saveSensor();
        cout << endl;
    }

    { //this cannot not find "saveSensorEdges()", so it won't compile
        BaseSensor<TrendSensor> eps;
        eps.saveSensor();
        cout << endl;
    }
    return 0;
}

我已经读到解决方案涉及遵循“SFINAE”规则,但是,SO 中的解决方案涉及键入特定于检查成员函数是否有效的代码(例如here)。是否可以通过检查是否包含 mixin 类(即TrendSensorEdgeSensor)来最小化编码?

我正在寻找一种在 c++11 中最小化额外编码(即创建多行结构以检查是否存在单个方法)的解决方案(很可能使用 boost)。

如果这是不可能的,我如何检查特定实例的函数是否存在并相应地执行(或不执行)。

基本上,任何东西都可以放在前面

    EXEC_ONLY_IF_EXISTS ( this->saveTrends(); )
    EXEC_ONLY_IF_EXISTS ( this->saveEdges(); )

为了有条件地允许代码并执行它,或者完全删除它,具体取决于 mixin 是否是实例化对象的一部分。 谢谢!

【问题讨论】:

    标签: c++ c++11 methods variadic-templates exists


    【解决方案1】:

    您可以在saveSensor() 中调用几个新方法:localTrends()localEdges()

    然后,您可以开发localTrends() 的两个替代实现(选择 SFINAE);第一个调用saveTrends(),仅当TrendSensor 是实际类的基类时启用,第二个不调用saveTrends(),否则(当TrendSensor 不是基类)。

    localEdges() 的相同策略:两个替代实现(选择 SFINAE),第一个调用 saveEdges(),仅当 EdgeSensor&lt;Something&gt; 是实际类的基类时启用,第二个,调用不调用saveEdges(),否则(当EdgeSensor&lt;Something&gt; 不是基类时)。

    localTrends() 的 SFINAE 选择很简单,使用 std::is_base_of

    localEdges() 的 SFINAE 选择有点复杂,因为您不能(或至少:我不知道如何)检查 EdgeSensor&lt;Something&gt; 是否是使用 std::is_base_of 的实际类的基类因为我不知道Something 类是EdgeSensor 的模板参数。

    所以我开发了一个模板structchkTplInL(用于“checkTemplateInList”)接收一个“模板模板”参数(即EdgeSensor,没有它的Something模板参数)和一个类型名列表.如果基于“模板模板”参数(在我们的例子中是EdgeSensor)的类在类型名列表中(在我们的例子中是:如果EdgeSensor 类是实际SensorType 类的基础),则false 否则。

    以下是一个工作示例

    #include <type_traits>
    #include <iostream>
    
    class PeakSensor { };
    class TroughSensor { };
    class TroughEdge { };
    
    template<typename EdgeType> 
    class EdgeSensor : public EdgeType
    { public: void saveEdges(){} };
    
    class TrendSensor 
    { public: void saveTrends(){} };
    
    template <template <typename ...> class, typename ...>
    struct chkTplInL;
    
    template <template <typename ...> class C>
    struct chkTplInL<C>
     { static constexpr bool value = false; };
    
    template <template <typename ...> class C, typename T0, typename ... Ts>
    struct chkTplInL<C, T0, Ts...>
     { static constexpr bool value = chkTplInL<C, Ts...>::value; };
    
    template <template <typename ...> class C, typename ... Ts1, typename ... Ts2>
    struct chkTplInL<C, C<Ts1...>, Ts2...>
     { static constexpr bool value = true; };
    
    template<typename ... SensorType>
    class BaseSensor : public SensorType ...
     {
       public:
          template <template <typename...> class C = EdgeSensor>
          typename std::enable_if<
             true == chkTplInL<C, SensorType...>::value>::type localEdges ()
           { this->saveEdges(); std::cout << "localEdges case A" << std::endl; }
    
          template <template <typename...> class C = EdgeSensor>
          typename std::enable_if<
             false == chkTplInL<C, SensorType...>::value>::type localEdges ()
           { std::cout << "localEdges case B" << std::endl; }
    
          template <typename B = TrendSensor>
          typename std::enable_if<
             true == std::is_base_of<B, BaseSensor>::value>::type localTrends ()
           { this->saveTrends(); std::cout << "localTrends case A" << std::endl; }
    
          template <typename B = TrendSensor>
          typename std::enable_if<
             false == std::is_base_of<B, BaseSensor>::value>::type localTrends ()
           { std::cout << "localTrends case B" << std::endl; }
    
          void saveSensor ()
           {
             this->localTrends();
             this->localEdges();
           }
     };
    
    
    int main () 
     {
       BaseSensor<EdgeSensor<TroughEdge> , TrendSensor> eps1;
       eps1.saveSensor();  // print localTrends case A
                           // and   localEdges case A
    
       BaseSensor<TrendSensor> eps2;
       eps2.saveSensor(); // print localTrends case A
                          // and   localEdges case B
    
       BaseSensor<EdgeSensor<TroughSensor>> eps3;
       eps3.saveSensor(); // print localTrends case B
                          // and   localEdges case A
    
       BaseSensor<> eps4;
       eps4.saveSensor(); // print localTrends case B
                          // and   localEdges case B
    
       return 0;
     }
    

    如果你可以使用 C++14 编译器,你可以使用std::enable_if_t,这样localEdges()localTrends() 的 SFINAE 选择可以简单一些

      template <template <typename...> class C = EdgeSensor>
      std::enable_if_t<
         true == chkTplInL<C, SensorType...>::value> localEdges ()
       { this->saveEdges(); std::cout << "localEdges case A" << std::endl; }
    
      template <template <typename...> class C = EdgeSensor>
      std::enable_if_t<
         false == chkTplInL<C, SensorType...>::value> localEdges ()
       { std::cout << "localEdges case B" << std::endl; }
    
      template <typename B = TrendSensor>
      std::enable_if_t<
         true == std::is_base_of<B, BaseSensor>::value> localTrends ()
       { this->saveTrends(); std::cout << "localTrends case A" << std::endl; }
    
      template <typename B = TrendSensor>
      std::enable_if_t<
         false == std::is_base_of<B, BaseSensor>::value> localTrends ()
       { std::cout << "localTrends case B" << std::endl; }
    

    【讨论】:

    • 嗯,这不能完全正常工作。如果 mixin 已内置到当前对象中,我想运行 mixin 的 save*() 方法。如果我没记错的话,这样我就不能创建多个 saveSensor 实例并全部执行(每个不同的 mixin 之一)
    • 为什么还要介绍一个新的PeakSensor() ?该类不应包含与保存有关的任何内容。此类任务由 EdgeSensor 或 TrendSensor(或其他)执行,但不是由 Peak 或 Trough ...
    • @nass - 不确定“如果 mixin 已内置到当前对象中,我想运行 mixin 的 save*() 方法”是什么意思。请问,你能创建一个最小但完整的例子吗?涉及所有课程?我只是举例介绍了一个新的 PeakSensor():我不知道是否定义了 saveSensorTrend()saveSensorEdges()
    • @nass - 回答您的问题“是否可以在 [...] 前面放置任何内容,以便有条件地允许代码并执行它,或者根据 mixin 是否完全删除它实例化对象的一部分 [?]”,如何定义两个本地方法 localSaveSensorTrend()localSaveSensorEdges(),每个方法有 2 个替代实现(通过 SFINAE 启用/禁用)其中localSaveSensorTrend() 调用 saveSensorTrend() 如果可用(版本 1)或否则什么都不做(第 2 版)?与localSaveSensorEdges() 相同的策略。
    • 哎呀有问题的严重错误。对于那个很抱歉。没有saveSensorTrend(),只有saveTrend()。对于边缘也是如此。我认为这应该现在解决它。至于什么意思,请参阅问题末尾的编辑:是否可以做类似的事情:EXEC_ONLY_IF_EXISTS( this-&gt;saveTrends(); ) 和类似的边缘?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-06
    • 2021-12-26
    • 2014-11-08
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多