多个 类 看起来几乎相同但使用一些不同的数据和函数
Multiple classes that look almost the same but use some different data and functions
我有多个 类 看起来几乎一样,做起来几乎一样,但定义和使用不同的数据,并且它们的一些功能工作不同(尽管它们具有相同的名称/签名)。
class SomeClass1{
class UniqueData1{};
UniqueData1 data;
static UniqueData1 static_data;
void doStuff1() {doWhatever( data);}
void doStuff2() {doUniqueStuff( data);}
void doUniqueStuff(UniqueData1 _data) {doABC( _data); doCDE();}
};
class SomeClass2{
class UniqueData2{};
UniqueData2 data;
static UniqueData2 static_data;
void doStuff1() {doWhatever( data);}
void doStuff2() {doUniqueStuff( data);}
void doUniqueStuff(UniqueData2 _data) {doMNO( _data); doPQR();}
};
像doStuff()这样的函数还有很多,还有很多类。如果没有一堆复制粘贴,我该如何解决这个问题?我在考虑模板和虚函数,但到目前为止还没有想出任何解决方案。
编辑:一些外观相同的函数需要调用执行独特操作的函数。
好吧,很难确定您要解决的真实问题是什么。但是,根据您给出的最低限度示例,这就是您使用模板的方式:
template <int I>
class SomeClass {
public:
class UniqueData{};
UniqueData data;
static UniqueData static_data;
void doStuff() { doWhatever(data); }
void doStuff2() { doUniqueStuff(data); }
void doUniqueStuff(UniqueData uniqueData);
};
void doCDE() { }
void doABC(SomeClass<1>::UniqueData) { }
void doPQR();
void doMNO(SomeClass<2>::UniqueData) { }
template<>
void::SomeClass<1>::doUniqueStuff(SomeClass<1>::UniqueData data) {
doABC(data);
doCDE();
}
template<>
void::SomeClass<2>::doUniqueStuff(SomeClass<2>::UniqueData data) {
doMNO(data);
doPQR();
}
int main() {
SomeClass<1>().doStuff2();
SomeClass<2>().doStuff2();
}
在这里,模板实例有自己独特的数据类型和两个函数。而且这两个函数是专门调用不同的函数的。
编辑:您指定希望 UniqueData
拥有一些独特的成员。如果你愿意灵活一点,你可以使用元组:
#include <functional>
#include <iostream>
#include <tuple>
#include <string>
template<typename T>
void doWhatever(T) { }
// This is now a variadic template. All arguments after id are packed into TUniqueDataMembers
template <int id, typename... TUniqueDataMembers>
class SomeClass {
public:
// UnqiueData has a tuple member (with TUniqueDataMembers). It has one member of each template argument given
class UniqueData {
public:
UniqueData() : data(std::make_tuple(TUniqueDataMembers{}...)) { }
std::tuple<TUniqueDataMembers...> data;
};
UniqueData data;
static UniqueData static_data;
void doStuff() { doWhatever(data); }
void doStuff2() { doUniqueStuff(data); }
void doUniqueStuff(UniqueData uniqueData);
};
// Using a type definition saves some boilerplate, now that we're using multiple template arguments
using SomeClass1 = SomeClass<1, int>;
using SomeClass2 = SomeClass<2, double, std::string>;
void doCDE() { }
void doABC(SomeClass1::UniqueData data) {
// Use std::get<index>(tuple) to access unique data
std::cout << "int: " << std::get<0>(data.data) << "\n";
}
void doPQR() { }
void doMNO(SomeClass2::UniqueData data) {
std::cout << "double: " << std::get<0>(data.data) << "\n";
std::cout << "string: " << std::get<1>(data.data) << "\n";
}
template<>
void SomeClass1::doUniqueStuff(SomeClass1::UniqueData data) {
doABC(data);
doCDE();
}
template<>
void SomeClass2::doUniqueStuff(SomeClass2::UniqueData data) {
doMNO(data);
doPQR();
}
int main() {
SomeClass1 class1 {};
std::get<0>(class1.data.data) = 42;
class1.doStuff2();
SomeClass2 class2 {};
std::get<0>(class2.data.data) = 2.5;
std::get<1>(class2.data.data) = "hello, world!";
class2.doStuff2();
}
编辑:关于 UniqueData
必须是具有不同成员的 ENUM 的约束……好吧,你让我明白了。模板无法进行 名称替换 。宏是唯一可以做到这一点的东西。不幸的是,C++ 宏不提供像可变参数模板那样的递归能力。
但是,使用可变宏技巧:Overloading Macro on Number of Arguments
您可以获得一个 "up-to-N" 参数解决方案,您可以根据需要对其进行扩展。这是最多 4 个枚举成员的实现:
#include <functional>
#include <iostream>
#include <tuple>
#include <string>
template<typename T>
void doWhatever(T) { }
// Variadic macro trick for "up-to-N-argument macros"
#define _GEN_ENUM1(_0) enum class UniqueData { _0 };
#define _GEN_ENUM2(_0, _1) enum class UniqueData { _0, _1 };
#define _GEN_ENUM3(_0, _1, _2) enum class UniqueData { _0, _1, _2 };
#define _GEN_ENUM4(_0, _1, _2, _3) enum class UniqueData { _0, _1, _2, _3 };
#define _GET_GEN_ENUM(_0, _1, _2, _3, NAME,...) NAME
#define _GEN_ENUM(...) _GET_GEN_ENUM(__VA_ARGS__, _GEN_ENUM4, _GEN_ENUM3, _GEN_ENUM2, GEN_ENUM1)(__VA_ARGS__)
// Macro to generate class variadic parameters pass to _GEN_ENUM
#define DECL_SOMECLASS(id, ...)\
class SomeClass##id{\
public:\
_GEN_ENUM(__VA_ARGS__)\
UniqueData data;\
static UniqueData static_data;\
void doStuff() { doWhatever(data); }\
void doStuff2() { doUniqueStuff(data); }\
void doUniqueStuff(UniqueData uniqueData);\
};
// Macro to implements SomeClass<N>::doStuff2() with custom function
#define IMPL_SOMECLASS(id, f_do_unique, f_do_not_unique)\
void SomeClass##id::doUniqueStuff(SomeClass##id::UniqueData data) {\
f_do_unique(data);\
f_do_not_unique();\
}
// Actually declare the classes
DECL_SOMECLASS(1, A, B, C)
DECL_SOMECLASS(2, D, E)
void doABC(SomeClass1::UniqueData data) {
std::cout << "A: " << (int)SomeClass1::UniqueData::A << "\n";
std::cout << "B: " << (int)SomeClass1::UniqueData::B << "\n";
std::cout << "C: " << (int)SomeClass1::UniqueData::C << "\n";
std::cout << "Data: " << (int)data << "\n";
}
void doCDE() { }
void doMNO(SomeClass2::UniqueData data) {
std::cout << "D: " << (int)SomeClass2::UniqueData::D << "\n";
std::cout << "E: " << (int)SomeClass2::UniqueData::E << "\n";
std::cout << "Data: " << (int)data << "\n";
}
void doPQR() { }
IMPL_SOMECLASS(1, doABC, doCDE)
IMPL_SOMECLASS(2, doMNO, doPQR)
int main() {
SomeClass1 class1 {};
class1.data = SomeClass1::UniqueData::C;
class1.doStuff2();
SomeClass2 class2 {};
class2.data = SomeClass2::UniqueData::E;
class2.doStuff2();
}
注意:通过使用宏而不是模板,您将失去 DECL_SOMECLASS
和 IMPL_SOMECLASS
参数的所有类型安全性。因此,您可以将 "lol"
作为 id
传递,并见证可怕的错误消息。这里是龙。
我有多个 类 看起来几乎一样,做起来几乎一样,但定义和使用不同的数据,并且它们的一些功能工作不同(尽管它们具有相同的名称/签名)。
class SomeClass1{
class UniqueData1{};
UniqueData1 data;
static UniqueData1 static_data;
void doStuff1() {doWhatever( data);}
void doStuff2() {doUniqueStuff( data);}
void doUniqueStuff(UniqueData1 _data) {doABC( _data); doCDE();}
};
class SomeClass2{
class UniqueData2{};
UniqueData2 data;
static UniqueData2 static_data;
void doStuff1() {doWhatever( data);}
void doStuff2() {doUniqueStuff( data);}
void doUniqueStuff(UniqueData2 _data) {doMNO( _data); doPQR();}
};
像doStuff()这样的函数还有很多,还有很多类。如果没有一堆复制粘贴,我该如何解决这个问题?我在考虑模板和虚函数,但到目前为止还没有想出任何解决方案。
编辑:一些外观相同的函数需要调用执行独特操作的函数。
好吧,很难确定您要解决的真实问题是什么。但是,根据您给出的最低限度示例,这就是您使用模板的方式:
template <int I>
class SomeClass {
public:
class UniqueData{};
UniqueData data;
static UniqueData static_data;
void doStuff() { doWhatever(data); }
void doStuff2() { doUniqueStuff(data); }
void doUniqueStuff(UniqueData uniqueData);
};
void doCDE() { }
void doABC(SomeClass<1>::UniqueData) { }
void doPQR();
void doMNO(SomeClass<2>::UniqueData) { }
template<>
void::SomeClass<1>::doUniqueStuff(SomeClass<1>::UniqueData data) {
doABC(data);
doCDE();
}
template<>
void::SomeClass<2>::doUniqueStuff(SomeClass<2>::UniqueData data) {
doMNO(data);
doPQR();
}
int main() {
SomeClass<1>().doStuff2();
SomeClass<2>().doStuff2();
}
在这里,模板实例有自己独特的数据类型和两个函数。而且这两个函数是专门调用不同的函数的。
编辑:您指定希望 UniqueData
拥有一些独特的成员。如果你愿意灵活一点,你可以使用元组:
#include <functional>
#include <iostream>
#include <tuple>
#include <string>
template<typename T>
void doWhatever(T) { }
// This is now a variadic template. All arguments after id are packed into TUniqueDataMembers
template <int id, typename... TUniqueDataMembers>
class SomeClass {
public:
// UnqiueData has a tuple member (with TUniqueDataMembers). It has one member of each template argument given
class UniqueData {
public:
UniqueData() : data(std::make_tuple(TUniqueDataMembers{}...)) { }
std::tuple<TUniqueDataMembers...> data;
};
UniqueData data;
static UniqueData static_data;
void doStuff() { doWhatever(data); }
void doStuff2() { doUniqueStuff(data); }
void doUniqueStuff(UniqueData uniqueData);
};
// Using a type definition saves some boilerplate, now that we're using multiple template arguments
using SomeClass1 = SomeClass<1, int>;
using SomeClass2 = SomeClass<2, double, std::string>;
void doCDE() { }
void doABC(SomeClass1::UniqueData data) {
// Use std::get<index>(tuple) to access unique data
std::cout << "int: " << std::get<0>(data.data) << "\n";
}
void doPQR() { }
void doMNO(SomeClass2::UniqueData data) {
std::cout << "double: " << std::get<0>(data.data) << "\n";
std::cout << "string: " << std::get<1>(data.data) << "\n";
}
template<>
void SomeClass1::doUniqueStuff(SomeClass1::UniqueData data) {
doABC(data);
doCDE();
}
template<>
void SomeClass2::doUniqueStuff(SomeClass2::UniqueData data) {
doMNO(data);
doPQR();
}
int main() {
SomeClass1 class1 {};
std::get<0>(class1.data.data) = 42;
class1.doStuff2();
SomeClass2 class2 {};
std::get<0>(class2.data.data) = 2.5;
std::get<1>(class2.data.data) = "hello, world!";
class2.doStuff2();
}
编辑:关于 UniqueData
必须是具有不同成员的 ENUM 的约束……好吧,你让我明白了。模板无法进行 名称替换 。宏是唯一可以做到这一点的东西。不幸的是,C++ 宏不提供像可变参数模板那样的递归能力。
但是,使用可变宏技巧:Overloading Macro on Number of Arguments 您可以获得一个 "up-to-N" 参数解决方案,您可以根据需要对其进行扩展。这是最多 4 个枚举成员的实现:
#include <functional>
#include <iostream>
#include <tuple>
#include <string>
template<typename T>
void doWhatever(T) { }
// Variadic macro trick for "up-to-N-argument macros"
#define _GEN_ENUM1(_0) enum class UniqueData { _0 };
#define _GEN_ENUM2(_0, _1) enum class UniqueData { _0, _1 };
#define _GEN_ENUM3(_0, _1, _2) enum class UniqueData { _0, _1, _2 };
#define _GEN_ENUM4(_0, _1, _2, _3) enum class UniqueData { _0, _1, _2, _3 };
#define _GET_GEN_ENUM(_0, _1, _2, _3, NAME,...) NAME
#define _GEN_ENUM(...) _GET_GEN_ENUM(__VA_ARGS__, _GEN_ENUM4, _GEN_ENUM3, _GEN_ENUM2, GEN_ENUM1)(__VA_ARGS__)
// Macro to generate class variadic parameters pass to _GEN_ENUM
#define DECL_SOMECLASS(id, ...)\
class SomeClass##id{\
public:\
_GEN_ENUM(__VA_ARGS__)\
UniqueData data;\
static UniqueData static_data;\
void doStuff() { doWhatever(data); }\
void doStuff2() { doUniqueStuff(data); }\
void doUniqueStuff(UniqueData uniqueData);\
};
// Macro to implements SomeClass<N>::doStuff2() with custom function
#define IMPL_SOMECLASS(id, f_do_unique, f_do_not_unique)\
void SomeClass##id::doUniqueStuff(SomeClass##id::UniqueData data) {\
f_do_unique(data);\
f_do_not_unique();\
}
// Actually declare the classes
DECL_SOMECLASS(1, A, B, C)
DECL_SOMECLASS(2, D, E)
void doABC(SomeClass1::UniqueData data) {
std::cout << "A: " << (int)SomeClass1::UniqueData::A << "\n";
std::cout << "B: " << (int)SomeClass1::UniqueData::B << "\n";
std::cout << "C: " << (int)SomeClass1::UniqueData::C << "\n";
std::cout << "Data: " << (int)data << "\n";
}
void doCDE() { }
void doMNO(SomeClass2::UniqueData data) {
std::cout << "D: " << (int)SomeClass2::UniqueData::D << "\n";
std::cout << "E: " << (int)SomeClass2::UniqueData::E << "\n";
std::cout << "Data: " << (int)data << "\n";
}
void doPQR() { }
IMPL_SOMECLASS(1, doABC, doCDE)
IMPL_SOMECLASS(2, doMNO, doPQR)
int main() {
SomeClass1 class1 {};
class1.data = SomeClass1::UniqueData::C;
class1.doStuff2();
SomeClass2 class2 {};
class2.data = SomeClass2::UniqueData::E;
class2.doStuff2();
}
注意:通过使用宏而不是模板,您将失去 DECL_SOMECLASS
和 IMPL_SOMECLASS
参数的所有类型安全性。因此,您可以将 "lol"
作为 id
传递,并见证可怕的错误消息。这里是龙。