可能的解决方案可以基于std::map自定义键有几个重载的运算符。
Example on Compiler Explorer
定义:
#include <iostream>
#include <map>
#include <vector>
template<typename GroupId, typename ValueId>
struct GroupKey
{
GroupId groupId{};
ValueId valueId{};
bool operator<(const GroupId &groupId) const
{
return this->groupId < groupId;
}
friend bool operator<(GroupId groupId, const GroupKey &key)
{
return groupId < key.groupId;
}
bool operator<(const GroupKey &other) const
{
if (groupId == other.groupId)
return valueId < other.valueId;
return groupId < other.groupId;
}
};
enum Group : uint16_t
{
GroupOne = 1,
GroupTwo,
GroupThree,
GroupFour
};
enum Value : uint16_t
{
One = 1,
Two,
Three
};
// NOTE: here it's necessery to use "transparent comparator".
// See notes below in the answer
using Values = std::map<GroupKey<Group, Value>, int, std::less<>>;
using Groups = std::vector<Group>;
Values values {
{{GroupOne, Two}, 1},
{{GroupOne, Three}, 2},
{{GroupTwo, One}, 3},
{{GroupTwo, Two}, 4},
{{GroupThree, Three}, 5},
{{GroupThree, Two}, 6},
{{GroupThree, One}, 7},
};
测试功能:
bool hasGroup(Group group)
{
return values.find(group) != values.cend();
}
bool hasValue(Group group, Value value)
{
return values.find({group, value}) != values.end();
}
Groups getGroups()
{
Groups groups;
if (values.empty())
return groups;
auto cit = values.cbegin();
auto cend = values.cend();
do {
groups.push_back(cit->first.groupId);
} while ((cit = values.upper_bound(cit->first.groupId)) != cend);
return groups;
}
int get(Group groupId, Value id, bool *isPresent = nullptr)
{
auto it = values.find({groupId, id});
bool present = it != values.end();
if (isPresent)
*isPresent = present;
return present ? it->second : int{};
}
bool insert(Group groupId, Value id, int value)
{
if (!hasValue(groupId, id)) {
values[{groupId, id}] = value;
return true;
}
return false;
}
void update(Group groupId, Value id, Value value)
{
// As we know, if here key is not present,
// it'll be inserted into the map
values[{groupId, id}] = value;
}
// Remove group (and all values that are releated this group)
void remove(Group groupId)
{
auto lower = values.lower_bound(groupId);
if (lower != values.cend())
values.erase(lower, values.upper_bound(groupId));
}
void remove(Group groupId, Value id)
{
values.erase({groupId, id});
}
测试:
int main()
{
// {{GroupOne, Two}, 1},
// {{GroupOne, Three}, 2},
// {{GroupTwo, One}, 3},
// {{GroupTwo, Two}, 4},
// {{GroupThree, Three}, 5},
// {{GroupThree, Two}, 6},
// {{GroupThree, One}, 7},
// Tests
std::cout << "Has GroupFour: " << hasGroup(GroupFour) << '\n'; // expected: false
std::cout << "Has GroupOne: " << hasGroup(GroupOne) << '\n'; // expected: true
std::cout << "Has One in GroupOne: " << hasValue(GroupOne, One) << '\n'; // expected: false
std::cout << "Has Two in GroupTwo: " << hasValue(GroupTwo, Two) << '\n'; // expected: true
auto groupsPrinter = [](const Groups &groups) {
std::cout << "Groups in the storage: ";
for (const auto &group : groups)
std::cout << group << ' ';
std::cout << std::endl;
};
groupsPrinter(getGroups()); // expected: 1(GroupOne), 2(GroupTwo), 3(GroupThree)
std::cout << "Inster GroupFour, One: " << insert(GroupFour, One, 8) << '\n';
std::cout << "Has One in GroupFour: " << hasValue(GroupFour, One) << '\n'; // expected: true
groupsPrinter(getGroups()); // expected: 1(GroupOne), 2(GroupTwo), 3(GroupThree), 4(GroupFour)
remove(GroupOne);
std::cout << "Has GroupOne: " << hasGroup(GroupOne) << '\n'; // expected: false
std::cout << "Has Two in GroupOne: " << hasValue(GroupOne, One) << '\n'; // expected: false
groupsPrinter(getGroups()); // expected: 2(GroupTwo), 3(GroupThree), 4(GroupFour)
remove(GroupFour, One);
std::cout << "Has GroupFour: " << hasGroup(GroupFour) << '\n'; // expected: false
std::cout << "Has One in GroupFour: " << hasValue(GroupOne, One) << '\n'; // expected: false
groupsPrinter(getGroups()); // expected: 2(GroupTwo), 3(GroupThree)
return 0;
}
输出:
Has GroupFour: 0
Has GroupOne: 1
Has One in GroupOne: 0
Has Two in GroupTwo: 1
Groups in the storage: 1 2 3
Inster GroupFour, One: 1
Has One in GroupFour: 1
Groups in the storage: 1 2 3 4
Has GroupOne: 0
Has Two in GroupOne: 0
Groups in the storage: 2 3 4
Has GroupFour: 0
Has One in GroupFour: 0
Groups in the storage: 2 3
一些注意事项:
有必要使用“透明比较器”,在我们的例子中,它是 std::less<> 作为值定义中的第三个模板参数。
为了更好地理解,让我们看一下std::map 的find() 方法的定义(在我使用的STL 实现中):
因此,为了能够使用不完全是Values::key_type 的类型调用find,或者换句话说,在我们的例子中,在以GroupId 作为参数的GroupKey 中调用operator<() 重载。
第二点是一组operator<():
-
map::lower_bound() 方法需要第一个bool operator<(const GroupId &groupId) const;
- 第二个
friend bool operator<(GroupId groupId, const GroupKey &key)map::upper_bound();
- 第三个
bool operator<(const GroupKey &other) const by std::map 本身,因为map::key_type 必须支持< 更少的操作。
就我而言,这 3 个是使其工作所必需的。
最后一点是关于storage.get(group) -> array<value>; 的要求,上面的示例中没有实现它,但可以用与Groups getGroups() 相同的方式完成。
我们可以使用upper 和lower 边界并在它们之间进行迭代以获取与组相关的值。它应该给我们两个 O(log n) 操作来获得边界 + 一个 O(m) 在它们之间进行迭代。
P.S.:我尽量保持示例简单,代码肯定可以改进,希望这个想法对某人有所帮助。