看起来几乎相同但使用一些不同数据和功能的多个类

时间:2019-11-20 18:59:08

标签: c++ templates virtual-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()和许多类。我如何解决此问题而无需复制粘贴?我当时在考虑模板和虚函数,但到目前为止还没有提出任何解决方案。

编辑:某些外观相同的函数需要调用执行唯一操作的函数。

1 个答案:

答案 0 :(得分:1)

好吧,要确定要解决的 real 问题真的很难。但是,根据您提供的最低限度的示例,这就是使用模板的方法:

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 您可以获得“最多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_SOMECLASSIMPL_SOMECLASS的参数的所有类型安全性。因此,您可以将"lol"作为id传递并目睹可怕的错误消息。这是龙。

相关问题