使用模板对单个 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中实现吗?
我想这样做的原因是
- 我不必特化适用于
ENUM::Single
和 ENUM::PAIR
的模板化方法,因为这些特化之间的所有差异都会对上述模式进行数学运算(避免大量代码重复)。
- 相同的模式在我的代码中经常出现,我可以避免在许多地方实施变通方法,这些变通方法在每种情况下几乎都是相同的。
您可以将常用方法添加到您的 类
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 方法中使用多个数据)。
假设模板构造如下:
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中实现吗?
我想这样做的原因是
- 我不必特化适用于
ENUM::Single
和ENUM::PAIR
的模板化方法,因为这些特化之间的所有差异都会对上述模式进行数学运算(避免大量代码重复)。 - 相同的模式在我的代码中经常出现,我可以避免在许多地方实施变通方法,这些变通方法在每种情况下几乎都是相同的。
您可以将常用方法添加到您的 类
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 方法中使用多个数据)。