std::make_pair、std::unordered_map 和从键类型移动构造函数的用法
std::make_pair, std::unordered_map and usage of move constructor from key type
给定以下结构:
struct FieldNo
{
FieldNo() : a('0'), b('0') {}
FieldNo(char a_, char b_) : a(a_), b(_b) {}
// copy construction and assigment not allowed
FieldNo(const FieldNo& other) = delete;
FieldNo& operator=(const FieldNo& other) = delete;
// move construction and assignment ok
FieldNo(FieldNo&& other) = default;
FieldNo& operator=(FieldNo&& other) = default;
char a;
char b;
};
enum class Members : int8_t
{
FOO,
BAR
};
我使用 FieldNo
作为键,Member
作为 std::unordered_map 的值。省略哈希创建函数的代码,我的地图定义如下:
typedef std::unordered_map<FieldNo, Members, FieldNoHasher> MyMapT;
稍后我使用以下方法初始化并return给调用者
const MyMapT& map()
{
static const MyMapT fields =
{
std::make_pair(FieldNo('0', '5'), Members::FOO),
std::make_pair(FieldNo('1', 'X'), Members::BAR)
}
return fields;
}
起初复制构造函数没有被删除,一切正常。
当我删除复制构造函数时,我在插入时遇到了大量错误,并将最相关的错误放在这里:
[build]
[build] /usr/include/c++/8/ext/new_allocator.h:136:4: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const FieldNo; _T2 = Members]’
[build] { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
[build] ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build] In file included from /usr/include/c++/8/bits/stl_algobase.h:64,
[build] from /usr/include/c++/8/bits/char_traits.h:39,
[build] from /usr/include/c++/8/ios:40,
[build] from /usr/include/c++/8/istream:38,
[build] from /usr/include/c++/8/fstream:38,
[build] from
[build] /usr/include/c++/8/bits/stl_pair.h:303:17: note: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const FieldNo; _T2 = Members]’ is implicitly deleted because the default definition would be ill-formed:
[build] constexpr pair(const pair&) = default;
[build] ^~~~
[build] /usr/include/c++/8/bits/stl_pair.h:303:17: error: use of deleted function ‘FieldNo::FieldNo(const libtraco::FieldNo&)’
我知道错误与缺少复制构造函数有关。但为什么它甚至试图复制东西呢?在std::make_pair
中的FieldNo('0', '5')
显然是rvalue
。
我错过了什么吗?
谢谢!
编辑:添加了最小的可重现示例
#include <unordered_map>
struct FieldNo
{
FieldNo() : a('0'), b('0') {}
FieldNo(char a_, char b_) : a(a_), b(b_) {}
// copy construction and assigment not allowed
FieldNo(const FieldNo& other) = delete;
FieldNo& operator=(const FieldNo& other) = delete;
// move construction and assignment ok
FieldNo(FieldNo&& other) = default;
FieldNo& operator=(FieldNo&& other) = default;
bool operator==(const FieldNo& lhs) const
{
return a == lhs.a && b == lhs.b;
}
char a;
char b;
};
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
struct FieldNoHasher
{
std::size_t operator()(const FieldNo& input) const
{
std::size_t seed = 0;
hash_combine(seed, input.a);
hash_combine(seed, input.b);
return seed;
}
};
enum class Members : int8_t
{
FOO,
BAR
};
typedef std::unordered_map<FieldNo, Members, FieldNoHasher> MyMapT;
const MyMapT& map()
{
static const MyMapT fields =
{
std::make_pair(FieldNo('0', '5'), Members::FOO),
std::make_pair(FieldNo('1', 'X'), Members::BAR)
};
return fields;
}
int main()
{
map();
}
这是一个最小的可重现示例:
#include <unordered_map>
#include <utility>
class C {
public:
C() = default;
C(const C&) = delete;
C& operator=(const C&) = delete;
C(C&&) = default;
C& operator=(C&&) = default;
};
bool operator==(const C&, const C&)
{
return true;
}
struct Hash {
constexpr std::size_t operator()(const C&) const
{
return 42;
}
};
int main()
{
std::unordered_map<C, int, Hash> map {
std::make_pair(C{}, 0)
};
}
问题是您正在调用 initializer_list
构造函数。由于 initializer_list
的工作方式,initializer_list
的基础元素是 const
,这意味着元素只能复制,不能移动。如果您的类型是仅移动,则不能使用 initializer_list
构造函数。您必须使用其他设施:
std::unordered_map<C, int, Hash> map;
map.emplace(C{}, 0);
或者您可以将initializer_list
替换为另一个支持移动的容器(例如向量):
std::vector<std::pair<const C, int>> values {
std::make_pair(C{}, 0)
};
std::unordered_map<C, int, Hash> map(values.cbegin(), values.cend());
给定以下结构:
struct FieldNo
{
FieldNo() : a('0'), b('0') {}
FieldNo(char a_, char b_) : a(a_), b(_b) {}
// copy construction and assigment not allowed
FieldNo(const FieldNo& other) = delete;
FieldNo& operator=(const FieldNo& other) = delete;
// move construction and assignment ok
FieldNo(FieldNo&& other) = default;
FieldNo& operator=(FieldNo&& other) = default;
char a;
char b;
};
enum class Members : int8_t
{
FOO,
BAR
};
我使用 FieldNo
作为键,Member
作为 std::unordered_map 的值。省略哈希创建函数的代码,我的地图定义如下:
typedef std::unordered_map<FieldNo, Members, FieldNoHasher> MyMapT;
稍后我使用以下方法初始化并return给调用者
const MyMapT& map()
{
static const MyMapT fields =
{
std::make_pair(FieldNo('0', '5'), Members::FOO),
std::make_pair(FieldNo('1', 'X'), Members::BAR)
}
return fields;
}
起初复制构造函数没有被删除,一切正常。
当我删除复制构造函数时,我在插入时遇到了大量错误,并将最相关的错误放在这里:
[build]
[build] /usr/include/c++/8/ext/new_allocator.h:136:4: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const FieldNo; _T2 = Members]’
[build] { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
[build] ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build] In file included from /usr/include/c++/8/bits/stl_algobase.h:64,
[build] from /usr/include/c++/8/bits/char_traits.h:39,
[build] from /usr/include/c++/8/ios:40,
[build] from /usr/include/c++/8/istream:38,
[build] from /usr/include/c++/8/fstream:38,
[build] from
[build] /usr/include/c++/8/bits/stl_pair.h:303:17: note: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const FieldNo; _T2 = Members]’ is implicitly deleted because the default definition would be ill-formed:
[build] constexpr pair(const pair&) = default;
[build] ^~~~
[build] /usr/include/c++/8/bits/stl_pair.h:303:17: error: use of deleted function ‘FieldNo::FieldNo(const libtraco::FieldNo&)’
我知道错误与缺少复制构造函数有关。但为什么它甚至试图复制东西呢?在std::make_pair
中的FieldNo('0', '5')
显然是rvalue
。
我错过了什么吗?
谢谢!
编辑:添加了最小的可重现示例
#include <unordered_map>
struct FieldNo
{
FieldNo() : a('0'), b('0') {}
FieldNo(char a_, char b_) : a(a_), b(b_) {}
// copy construction and assigment not allowed
FieldNo(const FieldNo& other) = delete;
FieldNo& operator=(const FieldNo& other) = delete;
// move construction and assignment ok
FieldNo(FieldNo&& other) = default;
FieldNo& operator=(FieldNo&& other) = default;
bool operator==(const FieldNo& lhs) const
{
return a == lhs.a && b == lhs.b;
}
char a;
char b;
};
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
struct FieldNoHasher
{
std::size_t operator()(const FieldNo& input) const
{
std::size_t seed = 0;
hash_combine(seed, input.a);
hash_combine(seed, input.b);
return seed;
}
};
enum class Members : int8_t
{
FOO,
BAR
};
typedef std::unordered_map<FieldNo, Members, FieldNoHasher> MyMapT;
const MyMapT& map()
{
static const MyMapT fields =
{
std::make_pair(FieldNo('0', '5'), Members::FOO),
std::make_pair(FieldNo('1', 'X'), Members::BAR)
};
return fields;
}
int main()
{
map();
}
这是一个最小的可重现示例:
#include <unordered_map>
#include <utility>
class C {
public:
C() = default;
C(const C&) = delete;
C& operator=(const C&) = delete;
C(C&&) = default;
C& operator=(C&&) = default;
};
bool operator==(const C&, const C&)
{
return true;
}
struct Hash {
constexpr std::size_t operator()(const C&) const
{
return 42;
}
};
int main()
{
std::unordered_map<C, int, Hash> map {
std::make_pair(C{}, 0)
};
}
问题是您正在调用 initializer_list
构造函数。由于 initializer_list
的工作方式,initializer_list
的基础元素是 const
,这意味着元素只能复制,不能移动。如果您的类型是仅移动,则不能使用 initializer_list
构造函数。您必须使用其他设施:
std::unordered_map<C, int, Hash> map;
map.emplace(C{}, 0);
或者您可以将initializer_list
替换为另一个支持移动的容器(例如向量):
std::vector<std::pair<const C, int>> values {
std::make_pair(C{}, 0)
};
std::unordered_map<C, int, Hash> map(values.cbegin(), values.cend());