std::map<K, V>::mapped_type有什么限制?
What are the restrictions on std::map<K, V>::mapped_type?
考虑这段代码:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class Foo {
public:
Foo() {}
virtual ~Foo() {}
void DoFoo() { cout << "Foo" << endl; }
Foo(const Foo&) = delete;
void operator=(const Foo&) = delete;
};
int main() {
map<string, Foo> m;
m["Foo"].DoFoo();
}
g++ 和 clang++ 在使用早于 4.8 的 libstdc++
版本时编译失败。 clang++ 吐出的确切错误消息是:
In file included from /usr/include/c++/4.6/iostream:39:
In file included from /usr/include/c++/4.6/ostream:39:
In file included from /usr/include/c++/4.6/ios:40:
In file included from /usr/include/c++/4.6/bits/char_traits.h:40:
In file included from /usr/include/c++/4.6/bits/stl_algobase.h:65:
/usr/include/c++/4.6/bits/stl_pair.h:121:35: error: call to deleted
constructor of 'Foo'
: first(std::forward<_U1>(__x)), second(__y) { }
^ ~~~
/usr/include/c++/4.6/bits/stl_pair.h:267:14: note: in instantiation of
function template specialization 'std::pair,
Foo>::pair, void>' requested here
return __pair_type(std::forward<_T1>(__x), std::forward<_T2>(__y));
^
/usr/include/c++/4.6/bits/stl_map.h:467:29: note: in instantiation of
function template specialization
'std::make_pair, Foo>' requested here
__i = insert(__i, std::make_pair(std::move(__k), mapped_type()));
^
21 : note: in instantiation of member function 'std::map, Foo,
std::less >, std::allocator, Foo> > >::operator[]' requested here
m["Foo"].DoFoo();
似乎 std::pair
的构造函数正在尝试使用 Foo
的复制构造函数,我认为这很公平,因为 Foo
没有声明移动构造函数.正如我所料,提供一个(默认)移动构造函数可以解决这个问题。
但是,当使用的 libstdc++
版本为 4.8 或更高版本时,即使没有定义移动构造函数,编译也会成功。我相信编译器在这两种情况下都是相同的,只有 libstdc++
版本不同。 Foo(Foo&&) = delete;
也不影响 clang 在这种情况下正确编译的能力。
我的问题有几个方面:
为什么旧版本的 libstdc++
需要用户提供移动构造函数才能使用它而不是复制构造函数?
较新版本的库有何不同,允许它在没有任何 move/copy 构造函数或 operator=
的情况下创建新元素(根据 operator[]
的合同)?
哪个实现符合要求?如果有的话,标准对 std::map<K, V>::mapped_type
说了什么?
在 C++11 中,[map.access] 表示:
T& operator[](const key_type& x);
1 Effects: If there is no key equivalent to x
in the map, inserts value_type(x, T())
into the map.
2 Requires: key_type
shall be CopyInsertable and mapped_type
shall be DefaultInsertable into
*this.
3 Returns: A reference to the mapped_type corresponding to x in *this.
4 Complexity: Logarithmic.
operator[]
对 mapped_type
的唯一要求是它是 DefaultInsertable(基本上是 DefaultConstructible)。如果库不支持不可复制的 mapped_type
和 operator[]
,那么它就是一个错误。
考虑这段代码:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class Foo {
public:
Foo() {}
virtual ~Foo() {}
void DoFoo() { cout << "Foo" << endl; }
Foo(const Foo&) = delete;
void operator=(const Foo&) = delete;
};
int main() {
map<string, Foo> m;
m["Foo"].DoFoo();
}
g++ 和 clang++ 在使用早于 4.8 的 libstdc++
版本时编译失败。 clang++ 吐出的确切错误消息是:
In file included from /usr/include/c++/4.6/iostream:39:
In file included from /usr/include/c++/4.6/ostream:39:
In file included from /usr/include/c++/4.6/ios:40:
In file included from /usr/include/c++/4.6/bits/char_traits.h:40:
In file included from /usr/include/c++/4.6/bits/stl_algobase.h:65:
/usr/include/c++/4.6/bits/stl_pair.h:121:35: error: call to deleted constructor of 'Foo'
: first(std::forward<_U1>(__x)), second(__y) { }
^ ~~~
/usr/include/c++/4.6/bits/stl_pair.h:267:14: note: in instantiation of function template specialization 'std::pair, Foo>::pair, void>' requested here
return __pair_type(std::forward<_T1>(__x), std::forward<_T2>(__y));
^
/usr/include/c++/4.6/bits/stl_map.h:467:29: note: in instantiation of function template specialization 'std::make_pair, Foo>' requested here
__i = insert(__i, std::make_pair(std::move(__k), mapped_type()));
^
21 : note: in instantiation of member function 'std::map, Foo, std::less >, std::allocator, Foo> > >::operator[]' requested here
m["Foo"].DoFoo();
似乎 std::pair
的构造函数正在尝试使用 Foo
的复制构造函数,我认为这很公平,因为 Foo
没有声明移动构造函数.正如我所料,提供一个(默认)移动构造函数可以解决这个问题。
但是,当使用的 libstdc++
版本为 4.8 或更高版本时,即使没有定义移动构造函数,编译也会成功。我相信编译器在这两种情况下都是相同的,只有 libstdc++
版本不同。 Foo(Foo&&) = delete;
也不影响 clang 在这种情况下正确编译的能力。
我的问题有几个方面:
为什么旧版本的 libstdc++
需要用户提供移动构造函数才能使用它而不是复制构造函数?
较新版本的库有何不同,允许它在没有任何 move/copy 构造函数或 operator=
的情况下创建新元素(根据 operator[]
的合同)?
哪个实现符合要求?如果有的话,标准对 std::map<K, V>::mapped_type
说了什么?
在 C++11 中,[map.access] 表示:
T& operator[](const key_type& x);
1 Effects: If there is no key equivalent to
x
in the map, insertsvalue_type(x, T())
into the map.2 Requires:
key_type
shall be CopyInsertable andmapped_type
shall be DefaultInsertable into *this.3 Returns: A reference to the mapped_type corresponding to x in *this.
4 Complexity: Logarithmic.
operator[]
对 mapped_type
的唯一要求是它是 DefaultInsertable(基本上是 DefaultConstructible)。如果库不支持不可复制的 mapped_type
和 operator[]
,那么它就是一个错误。