使用模板对单个 C++ 对象和 std::pair 个对象进行抽象

Abstraction over single c++ object and std::pair of objects using templates

假设模板构造如下:

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<ENUM::PAIR, T> 重新实现 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. 相同的模式在我的代码中经常出现,我可以避免在许多地方实施变通方法,这些变通方法在每种情况下几乎都是相同的。

您可以将常用方法添加到您的 类

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

现在 someMethod 只会调用右边的 "handle" 并且会自动在两者之间切换

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

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

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

#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;
}

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

#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的成员方法外,数据还可以用其他任何方式使用。

我很高兴对此解决方案发表评论(如果可以将其推广为在 for_single_or_pair 方法中使用多个数据)。