模板图的设计问题采用不同的结构
Design issue with template map with takes different structures
问题描述和问题
我有一个template
类Class1
。它包含在map
中,我想在其中插入结构A
或B
。
问题是结构A
和B
具有不同类型的成员变量。结构A
具有std::string
成员变量,而结构B
具有int
成员变量。
比较器基于结构A
。所以很明显,当我想插入一个结构B时,它不会编译。
Class1<B,B> c2;
c2.AddElement({1},{1});
如何解决该设计问题?例如,是否可以将Class1
保留为template
类,并对TestCompare
做些什么?
我也有一个约束I不能修改结构A
和B
。它们是用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
专门用于A
和B
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;
同时仍然保持在必要时提供另一比较器的能力。