无法在 unordered_map 个引用中插入非常量值
Can't insert non-const value in unordered_map of references
我正在尝试创建 2 个 std::unordered_map,一个包含 ,第二个包含 。
如果你好奇的话,我会在最后解释为什么我想这样做。
我的问题是 k_i 具有类型 std::reference_wrapper 的值,k_i.insert 不起作用。但是,如果我使 k_i 具有值 std::reference_wrapper<const A>,则插入有效。
我只是想不通这是为什么,我很好奇。
<<<<<编辑:
问题是找到 returns std::pair<const Ket, T>
如
所述
Eljay 在评论中。因此,第二个 std::unordered_map 需要具有值 const。
<<<<<
代码:
编译器:g++ 版本 10.1
编译标志:-Wall -Wextra -std=c++20
#include <unordered_map>
#include <iostream>
#include <string>
#include <functional>
class A {
public:
A(const int x) : x(x) {
std::cout << "A::A(const int x) : x(" << x << ")\n";
}
A(const A& a) {
std::cout << "A::A {" << x << "} (const A& a {" << a.x << "} )\n";
x = a.x;
}
A(A&& a) {
std::cout << "A::A {" << x << "} (A&& a {" << a.x << "} )\n";
x = a.x;
}
A& operator=(const A& a) {
std::cout << "A::operator= {" << x << "} (const A& a)\n";
x = a.x;
return *this;
}
A& operator=(A&& a) {
std::cout << "A::operator= {" << x << "} (A&& a)\n";
x = a.x;
return *this;
}
~A() {
std::cout << "A::~A(" << x << ")\n";
}
friend std::ostream& operator<<(std::ostream& os, const A& dt);
int x;
};
std::ostream& operator<<(std::ostream& os, const A& dt) {
return os << dt.x;
}
template <typename K, typename V, typename... args>
void print_um(const std::unordered_map<K, V, args...> &umap) {
for (const auto &[x, y] : umap) {
std::cout << "(" << x << "," << std::ref(y).get() << "); ";
}
std::cout << "\n";
}
template <typename T>
struct MyHash {
std::size_t operator()(T const& s) const noexcept {
return std::hash<int>{}(std::ref(s).get());
}
};
template <typename T>
struct MyEquals {
constexpr bool operator()(const T &lhs, const T &rhs) const {
return lhs == rhs;
}
};
struct MyHash_A {
std::size_t operator()(A const& s) const noexcept {
return std::hash<int>{}(s.x);
}
};
struct MyEquals_A {
constexpr bool operator()(const A &lhs, const A &rhs) const {
return lhs.x == rhs.x;
}
};
int main() {
std::unordered_map<A, int, MyHash_A, MyEquals_A> k_s;
std::unordered_map<std::reference_wrapper<int>, std::reference_wrapper<const A>, MyHash<std::reference_wrapper<int>>, MyEquals<std::reference_wrapper<int>>> k_i;
{
A a(5);
std::cout << "1----\n";
k_s[a] = 12;
std::cout << "2----\n";
}
std::cout << "3----\n";
print_um<>(k_s);
std::cout << "4----\n";
A a(5);
std::cout << "5----\n";
auto it = k_s.find(a);
std::cout << "6----\n";
k_i.emplace((*it).second, (*it).first);
// // k_i[(*it).second] = ref_name;
std::cout << "7----\n";
print_um<>(k_s);
std::cout << "8----\n";
print_um<>(k_i);
std::cout << "9----\n";
int x = 12;
int &ref = x;
auto is_there = k_i.find(ref);
if (is_there != k_i.end()) {
std::cout << "elem: " << (*is_there).second.get() << "\n";
} else {
std::cout << "why? :(\n";
}
std::cout << "10---\n";
return 0;
}
至于为什么我创建这段代码,我想能够通过值或键互换地访问一些数据(是否有更好的数据结构?)。就像用户名和令牌一样,有时我有一个,有时我有另一个,并且使用引用确保我不会浪费 space。 Ofc,如果必须更改一个值,我会因为密钥而使 unordered_map 中的存储桶位置无效,但我会在以后处理该问题。另一个动机是学习更多的 C++ 并测试它的极限(或我的极限)。
来自 UnorderedAssociativeContainer requirements:
For std::unordered_map
and std::unordered_multimap
the value type is std::pair<const Key, T>
.
在您的代码中 k_s
是 unordered_map<A, int>
,因此值类型是 pair<const A, int>
。在这里:
auto it = k_s.find(a);
你得到一个指向这样一对的“指针”,(*it).first
的类型是 const A
。
您的 k_i
是 unordered_map<..., ref<A>>
,当您在此处插入时:
k_i.emplace(..., (*it).first);
您实际上是在尝试用 const A
初始化 ref<A>
,这显然行不通。
当您将 k_i
类型更改为 unordered_map<..., ref<const A>>
时,您将 ref<const A>
初始化为 const A
,这很好。
很多人在评论中提到Key type必须是const
。然而,OP 似乎想知道为什么 k_i
中的值类型也需要是 const
.
这是因为您正在引用 k_s
中的键,即 const A
,您不能使用非常量引用来引用它。
要正确声明 k_s
,您可能需要执行以下操作:
std::unordered_map<
std::reference_wrapper<decltype(k_s)::value_type::second_type>,
std::reference_wrapper<decltype(k_s)::value_type::first_type>,
yourHash, yourComp
> k_s;
您的一个替代解决方案是使用另一个实际支持双向查找的容器,例如 Boost.Bimap.
我正在尝试创建 2 个 std::unordered_map,一个包含 ,第二个包含
我的问题是 k_i 具有类型 std::reference_wrapper 的值,k_i.insert 不起作用。但是,如果我使 k_i 具有值 std::reference_wrapper<const A>,则插入有效。
我只是想不通这是为什么,我很好奇。
<<<<<编辑:
问题是找到 returns std::pair<const Ket, T>
如
所述
Eljay 在评论中。因此,第二个 std::unordered_map 需要具有值 const。
<<<<<
代码: 编译器:g++ 版本 10.1 编译标志:-Wall -Wextra -std=c++20
#include <unordered_map>
#include <iostream>
#include <string>
#include <functional>
class A {
public:
A(const int x) : x(x) {
std::cout << "A::A(const int x) : x(" << x << ")\n";
}
A(const A& a) {
std::cout << "A::A {" << x << "} (const A& a {" << a.x << "} )\n";
x = a.x;
}
A(A&& a) {
std::cout << "A::A {" << x << "} (A&& a {" << a.x << "} )\n";
x = a.x;
}
A& operator=(const A& a) {
std::cout << "A::operator= {" << x << "} (const A& a)\n";
x = a.x;
return *this;
}
A& operator=(A&& a) {
std::cout << "A::operator= {" << x << "} (A&& a)\n";
x = a.x;
return *this;
}
~A() {
std::cout << "A::~A(" << x << ")\n";
}
friend std::ostream& operator<<(std::ostream& os, const A& dt);
int x;
};
std::ostream& operator<<(std::ostream& os, const A& dt) {
return os << dt.x;
}
template <typename K, typename V, typename... args>
void print_um(const std::unordered_map<K, V, args...> &umap) {
for (const auto &[x, y] : umap) {
std::cout << "(" << x << "," << std::ref(y).get() << "); ";
}
std::cout << "\n";
}
template <typename T>
struct MyHash {
std::size_t operator()(T const& s) const noexcept {
return std::hash<int>{}(std::ref(s).get());
}
};
template <typename T>
struct MyEquals {
constexpr bool operator()(const T &lhs, const T &rhs) const {
return lhs == rhs;
}
};
struct MyHash_A {
std::size_t operator()(A const& s) const noexcept {
return std::hash<int>{}(s.x);
}
};
struct MyEquals_A {
constexpr bool operator()(const A &lhs, const A &rhs) const {
return lhs.x == rhs.x;
}
};
int main() {
std::unordered_map<A, int, MyHash_A, MyEquals_A> k_s;
std::unordered_map<std::reference_wrapper<int>, std::reference_wrapper<const A>, MyHash<std::reference_wrapper<int>>, MyEquals<std::reference_wrapper<int>>> k_i;
{
A a(5);
std::cout << "1----\n";
k_s[a] = 12;
std::cout << "2----\n";
}
std::cout << "3----\n";
print_um<>(k_s);
std::cout << "4----\n";
A a(5);
std::cout << "5----\n";
auto it = k_s.find(a);
std::cout << "6----\n";
k_i.emplace((*it).second, (*it).first);
// // k_i[(*it).second] = ref_name;
std::cout << "7----\n";
print_um<>(k_s);
std::cout << "8----\n";
print_um<>(k_i);
std::cout << "9----\n";
int x = 12;
int &ref = x;
auto is_there = k_i.find(ref);
if (is_there != k_i.end()) {
std::cout << "elem: " << (*is_there).second.get() << "\n";
} else {
std::cout << "why? :(\n";
}
std::cout << "10---\n";
return 0;
}
至于为什么我创建这段代码,我想能够通过值或键互换地访问一些数据(是否有更好的数据结构?)。就像用户名和令牌一样,有时我有一个,有时我有另一个,并且使用引用确保我不会浪费 space。 Ofc,如果必须更改一个值,我会因为密钥而使 unordered_map 中的存储桶位置无效,但我会在以后处理该问题。另一个动机是学习更多的 C++ 并测试它的极限(或我的极限)。
来自 UnorderedAssociativeContainer requirements:
For
std::unordered_map
andstd::unordered_multimap
the value type isstd::pair<const Key, T>
.
在您的代码中 k_s
是 unordered_map<A, int>
,因此值类型是 pair<const A, int>
。在这里:
auto it = k_s.find(a);
你得到一个指向这样一对的“指针”,(*it).first
的类型是 const A
。
您的 k_i
是 unordered_map<..., ref<A>>
,当您在此处插入时:
k_i.emplace(..., (*it).first);
您实际上是在尝试用 const A
初始化 ref<A>
,这显然行不通。
当您将 k_i
类型更改为 unordered_map<..., ref<const A>>
时,您将 ref<const A>
初始化为 const A
,这很好。
很多人在评论中提到Key type必须是const
。然而,OP 似乎想知道为什么 k_i
中的值类型也需要是 const
.
这是因为您正在引用 k_s
中的键,即 const A
,您不能使用非常量引用来引用它。
要正确声明 k_s
,您可能需要执行以下操作:
std::unordered_map<
std::reference_wrapper<decltype(k_s)::value_type::second_type>,
std::reference_wrapper<decltype(k_s)::value_type::first_type>,
yourHash, yourComp
> k_s;
您的一个替代解决方案是使用另一个实际支持双向查找的容器,例如 Boost.Bimap.