【发布时间】:2021-09-14 05:24:17
【问题描述】:
我有一个 模板 类 (BiMap),它被用作 双向 地图,用于 查找目的,例如enum 值映射到 std::string 等效项,反之亦然。
要实现这一点,std::string 值必须也是唯一的,以防止重复的 std::string 值在 按值搜索 查找期间返回第一个找到的 enum 键.
template<typename Key, typename Value>
class BiMap {
public:
explicit BiMap(std::initializer_list<std::pair<Key, Value>> &&items) : bimap_{items.begin(), items.end()} {
assert(!HasDuplicates(bimap_));
}
Key GetKeyFromValue(const Value &value) const {
auto it = std::find_if(bimap_.begin(), bimap_.end(), [&value](const std::pair<Key, Value> &kvp) {
return kvp.second == value;
});
return (it != bimap_.end() ? it->first : Key());
}
Value GetValueFromKey(const Key &key) const {
auto it = bimap_.find(key);
return (it != bimap_.end() ? it->second : Value());
}
private:
const std::map<Key, Value> bimap_;
};
我使用一个名为 HasDuplicates 的函数来检查任何重复值:
template<typename Key, typename Value>
bool HasDuplicates(const std::map<Key, Value> &bimap) {
// Create a map to use the values as keys
std::map<Value, Key> value_key_map;
for (auto &kvp : bimap) value_key_map.emplace(kvp.second, kvp.first);
// If there are no duplicate values then the sizes should be the same
std::cout << "HasDuplicates: " << std::boolalpha << (value_key_map.size() != bimap.size()) << std::endl;
return (value_key_map.size() != bimap.size());
}
我可以运行以下示例代码,它会指示在运行时是否有任何重复值:
// Test 1: No duplicates
std::cout << "**No duplicates test:**" << std::endl;
const BiMap<std::string, int> bi_map_no_dups({{"foo", 1}, {"bar", 2}, {"foobar", 3}});
std::cout << "foo: " << bi_map_no_dups.GetValueFromKey("foo") << std::endl;
std::cout << "bar: " << bi_map_no_dups.GetValueFromKey("bar") << std::endl;
std::cout << "foobar: " << bi_map_no_dups.GetValueFromKey("foobar") << std::endl;
// Test 2: Duplicates
std::cout << "**Duplicates test:**" << std::endl;
const BiMap<std::string, int> bi_map_dups({{"foo", 1}, {"bar", 2}, {"foobar", 1}});
std::cout << "foo: " << bi_map_dups.GetValueFromKey("foo") << std::endl;
std::cout << "bar: " << bi_map_dups.GetValueFromKey("bar") << std::endl;
std::cout << "foobar: " << bi_map_dups.GetValueFromKey("foobar") << std::endl;
这样的输出是:
**No duplicates test:**
HasDuplicates: false
foo: 1
bar: 2
foobar: 3
**Duplicates test:**
HasDuplicates: true
main.cpp:22: BiMap<Key, Value>::BiMap(std::initializer_list<std::pair<_T1, _T2> >&&) [with Key = std::basic_string<char>; Value = int]: Assertion `!HasDuplicates(bimap_)' failed.
上述代码的工作示例可以在here找到。
问题:
如何评估std::map在编译时是否有重复值?
我尝试过的:
我已经尝试实现constexpr模板函数,如here:
template <typename K, typename V> constexpr bool has_duplicates(const std::map<K,V> *map)
{
std::map<V,K> value_key_map;
for(auto &kvp : map) value_key_map.emplace(map->second,map->first);
return map->size() == value_key_map.size();
}
int main() {
// Cannot get this part to work
constexpr std::map<std::string, int> bimap({{"foo", 1}, {"bar", 2}, {"foobar", 1}});
static_assert(!has_duplicates(&bimap));
return 0;
}
注意:我正在使用 C++11,我还不能声明 local 变量和 循环在 constexpr 函数内部,因此应该恢复为递归,如 here 所示。但是,对于这个例子,如果我能找到一个具有 C++14 的 constexpr 功能的合适解决方案,我会很高兴,并且稍后我会得到一个递归版本(如果可能的话)。
【问题讨论】:
-
std::map和std::string不是constexpr(std::string可能仅在 C++20 后用于constexpr函数)。 -
如果你变成
std::array<std::pair<const K, V>, N>(实际上是你自己的数组,因为在C++11中缺少constexpr),你可以在编译时进行检查。 -
@Jarod42 你是完全正确的。这似乎是不可能的。但是,我确实使用了您的 std::array 提案here,它是仅使用 GCC10.x 及更高版本的 C++11 和 C++14 的可行版本。
-
请注意,您实际上需要自定义函数来比较 C 字符串。
"FOO" == "FOO"不保证是true。 -
@Jarod42,非常感谢。根据您的最后一条评论,我能够创建一个合适的 solution,它支持 GNU 4.8.5 和 C++11 以及 C++14 及更高版本的新编译器。
标签: c++ c++11 constexpr stdmap