【问题标题】:Abstraction over single c++ object and std::pair of objects using templates使用模板对单个 c++ 对象和 std::pair 对象进行抽象
【发布时间】:2015-04-24 08:45:35
【问题描述】:

假设以下模板构造:

enum class ENUM {SINGLE, PAIR};
// General data type
template<ENUM T, class U>class Data;
// Partially specialized for single objects
template<class U>Data<ENUM::SINGLE, U> : public U {
  // Forward Constructors, ...
};
// Partially specialized for pairs of objects
template<class U>Data<ENUM::PAIR, U> : public std::pair<U,U> {
  // Forward Constructors, ...
};

在我的代码中,我希望能够编写类似的东西

template<ENUM T>someMethod(Data<T, SomeClass> data) {
  for_single_or_pair {
    /*
     * Use data as if it would be of type SomeClass
     */
  }
}

这应该和以下方法的组合一样:

template<>someMethod(Data<ENUM::SINGLE, SomeClass> data) {
  data.doStuff();
}

template<>incrementData(Data<ENUM::PAIR, SomeClass> data) {
  data.first.doStuff();
  data.second.doStuff();
}

即我希望能够使用一对(相同类型的)对象,就好像它是一个对象一样。当然,我可以为 Data&lt;ENUM::PAIR, T&gt; 重新实现 T 类型的方法(参见 dau_sama 的答案),对于给定的示例,它看起来像:

template<>Data<ENUM::PAIR, SomeClass> : public std::pair<SomeClass, SomeClass> {
  doStuff() {
    this->first.doStuff();
    this->second.doStuff();
  }
};

但是我必须为许多方法和运算符以及许多不同的类型执行此操作,尽管方法和运算符看起来都像这个示例。

解决方案的语法可能与我上面写的很不一样,这只是为了演示我想要实现的目标。我更喜欢没有宏的解决方案,但也可以接受。

这样的抽象可以在 C++11 中实现吗?

我想这样做的原因是

  1. 我不必专门化适用于 ENUM::SingleENUM::PAIR 的模板化方法,因为专门化之间的所有差异都会计算出上述模式(避免大量代码重复)。
  2. 我的代码中经常出现相同的模式,我可以避免在许多地方实施变通办法,这在每种情况下几乎相同。

【问题讨论】:

    标签: c++ c++11 abstraction


    【解决方案1】:

    您可以尝试创建一个模板方法applyMethod。这是一个完整的例子。我使用了一个只包含一个静态方法的 Executor 类,因为我找不到更好的方法来处理采用任何类型参数的方法

    #include <iostream>
    #include <string>
    
    enum ENUM {SINGLE, PAIR};
    // General data type
    template<ENUM T, class U>class Data {
    };
    // Partially specialized for single objects
    template<class U>
    class UData : public Data<ENUM::SINGLE, U>, public U {
      // Forward Constructors, ...
    public:
            UData(const U& u): U(u) {};
    };
    // Partially specialized for pairs of objects
    template<class U>
    class PData : public Data<ENUM::PAIR, U>, public std::pair<U,U> {
      // Forward Constructors, ...
    public:
            PData(const U& u1, const U& u2): std::pair<U, U>(u1, u2) {};
    };
    
    template <class U, typename... P>
    class Executor {
            Executor() = delete;
    public:
            template<void (U::*M)(P... params)>
            static void applyMethod(Data<ENUM::SINGLE, U> &data, P ...params) {
                    UData<U>& ud= reinterpret_cast<UData<U>& >(data);
                    U& u = static_cast<U&>(ud);
                    (u.*M)(params...);
            }
            template<void (U::*M)(P... params)>
            static void applyMethod(Data<ENUM::PAIR, U> &data, P ...params) {
                    PData<U>& pd = reinterpret_cast<PData<U>& >(data);
                    (pd.first.*M)(params...);
                    (pd.second.*M)(params...);
            }
    };
    
    class X {
            std::string name;
    public:
            X(const std::string& name): name(name) { };
    
            void doStuff(void) {
                    std::cout << "DoStuff : " << name << std::endl;
            }
            void doStuff(int i) {
                    std::cout << "DoStuff : " << name << " - " << i << std::endl;
            }
    };
    
    int main() {
            X x1("x1");
            X x2("x2");
            X x3("x3");
    
            UData<X> data1(x1);
            PData<X> data2(x2, x3);
    
            Executor<X>::applyMethod<&X::doStuff>(data1);
            Executor<X, int>::applyMethod<&X::doStuff>(data2, 12);
    
            return 0;
    }
    

    【讨论】:

    • 我刚刚注意到您创建了 UDataPData 类,因此我不能直接使用 Data&lt;T,SomeClass&gt; data,即我不能调用 Executor&lt;T, SomeClass&gt;::applyMethod&lt;&amp;SomeClass::doStuff&gt;(data),除非实际确定了 T在代码中。所以我还是无法避免someMethod的专业化,可以吗?
    • @user2296653:是的,你可以!我刚刚找到了一种方法,其中 Executor 类是唯一的,并且根据传递给它的实际数据使用正确的方法。
    【解决方案2】:

    你可以为你的类添加一个通用方法

    template<class U>
    Data<ENUM::SINGLE, U> : public U {
      // Forward Constructors, ...
      void handle() {
        //do some specific handling for this type 
        return;
      }
    };
    

    现在 someMethod 只会调用正确的“句柄”,它会自动在两者之间切换

    template<typename T>
    someMethod(T& data) {
      data.handle();
    }
    
    //If you want to bind your function to some other name, you could
    //create a functor that calls someMethod with the arguments passed in _1
    //I haven't tested it, there might be some syntax problems with the way you pass in the function name
    auto someOtherMethod = std::bind (someMethod, _1);
    

    如果您的类型没有实现句柄方法,您将遇到严重的编译错误。如果您想提供默认实现并避免编译错误,有一种称为 SFINAE(替换失败不是错误)的常见模式可以做到这一点。

    【讨论】:

    • 这并不是我想要达到的(见我的编辑)。
    • 那么您可以做的就是拥有一个自定义方法而不是 operator++。让我编辑我的答案。
    • 我注意到我的问题不够精确。因此,不幸的是,您更新的答案仍然不是我想要的(请参阅我的编辑 2)。
    • 检查我的编辑!不过,我可以争辩说,如果您有多个名称不同的方法,都在做同样的事情,那么就会出现设计问题。可能是您被绑定在一个您无法触及的遗留 API 上……那是另一回事 :-)
    • 如果您需要在不同类型之间切换,您总是必须提供专门化,因此您必须在某处编写处理这种情况的方法。如果您只想从不同类型调用相同的方法,则可以使用模板化的朋友函数。或者您可以使用特征类在不同类型之间切换。
    【解决方案3】:

    这是 Serge Ballesta 的解决方案的替代方案,使用 lambdas。

    #include <functional>
    
    template<ENUM T, class U>void for_single_or_pair(
        Data<T, U>& data,
        std::function<void(U&)> function);
    
    template<class U>void for_single_or_pair(
        Data<ENUM::SINGLE, U>& data,
        std::function<void(U&)> function) {
      function(data);
    }
    
    template<class U>void for_single_or_pair(
        Data<ENUM::PAIR, U>& data,
        std::function<void(U&)> function) {
      function(data.first);
      function(data.second);
    }
    

    用法:

    template<ENUM T>someMethod(Data<T, SomeClass> data) {
      for_single_or_pair(data,[](SomeClass& someObject) {
        // Play around with someObject in any way
      });
    }
    

    这样除了使用SomeClass的成员方法外,数据还可以以任何其他方式使用。

    我会很高兴 cmets 对此解决方案(如果它可以推广到在 for_single_or_pair 方法中使用多个数据)。

    【讨论】:

      猜你喜欢
      • 2016-07-23
      • 1970-01-01
      • 2017-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-24
      相关资源
      最近更新 更多