模板图的设计问题采用不同的结构

Design issue with template map with takes different structures

本文关键字:结构 问题      更新时间:2023-10-16

问题描述和问题

我有一个templateClass1。它包含在map中,我想在其中插入结构AB

问题是结构AB具有不同类型的成员变量。结构A具有std::string成员变量,而结构B具有int成员变量。

比较器基于结构A。所以很明显,当我想插入一个结构B时,它不会编译。

Class1<B,B> c2;
c2.AddElement({1},{1});

如何解决该设计问题?例如,是否可以将Class1保留为template类,并对TestCompare做些什么?

我也有一个约束I不能修改结构AB。它们是用C代码编写的。我无权更改它们,因为它们是其他用户使用的外部代码。我只是尽可能简化了代码。


源代码

代码是在cpp.sh 上编译的

#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;

template<typename T1, typename T2> class Class1 {
public :
struct TestCompare {
bool operator()(const T1 & lhs, const T1 & rhs) const {
return lhs.a < rhs.a;
}
};
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 & value) {
m.emplace(key, value);
}
private :
std::map<T1,T2,TestCompare> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});

// Problem here. Obviously it will not compile because the Operator is using 
// the member variable of struct A.
//Class1<B,B> c2;
//c2.AddElement({1},{1});
//return 0;
}

新源代码

// Example program
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;

bool operator<(const A & lhs, const A & rhs) {
return lhs.a < rhs.a;
}
bool operator<(const B & lhs, const B & rhs) {
return lhs.b < rhs.b;
}
template<typename T1, typename T2> class Class1 {
public :
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 value) {
m.emplace(key, value);
}

std::map<T1,T2> getMap() {
return m;
}

private :
std::map<T1,T2> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});

// Problem here. Obviously it will not compile because the Operator is using 
// the member variable of struct A.
Class1<B,B> c2;
c2.AddElement({1},{1});
c2.AddElement({2},{2});

for(const auto &e: c2.getMap()) {
std::cout << e.first.b << " " << e.first.b << std::endl;
}
return 0;
}

我想你可以从Class1中删除TestCompare并将其模板化。

template<typename T> struct TestCompare {
bool operator()(const T & lhs, const T & rhs) const {
// default implementation
return lhs < rhs;
}
};
template<typename T1, typename T2> class Class1 {
...
private :
std::map<T1,T2,TestCompare<T1>> m;
}

然后,您可以将TestCompare专门用于AB

template<> struct TestCompare<A> {
bool operator()(const A & lhs, const A & rhs) const {
return lhs.a < rhs.a;
}
};
template<> struct TestCompare<B> {
bool operator()(const B & lhs, const B & rhs) const {
return lhs.b < rhs.b;
}
};

编辑:实际上,您可以使用std::less而不是TestCompare。这基本上是一样的,默认情况下std::map使用std::less

TestCompare要求您使用的每个类型都必须有一个可以使用<进行比较的成员a。这是一个很大的要求,这意味着一个糟糕的设计。添加第三个模板参数,该参数将用于传递比较对象的函数或函子

struct CompareA {
bool operator()(A const & lhs, A const & rhs) const {
return lhs.a < rhs.a;
}
};
struct CompareB {
bool operator()(B const& lhs, B const& rhs) const {
/*...*/
}
};
template<typename KeyT, typename ValueT, typename Compare> class Dict {
public :
Class1() {}
~Class1() {}
void AddElement(KeyT const & key, ValueT const & value) {
m.emplace(key, value);
}
private :
std::map<KeyT, ValueT, Compare> m;
};
Dict<A, B, CompareA> dictA;
Dict<B, B CompareB> dictB;

您可以专门化结构TestCompare,就像john在回答中建议的那样,并将其作为默认模板参数提供

template<typename KeyT, typename ValueT, typename Compare = TestCompare<KeyT>> class Dict { /*...*/ };

这样的解决方案将允许您只提供2个参数,如

Dict<B, B> dict;

同时仍然保持在必要时提供另一比较器的能力。