您现在的位置是:首页 >技术交流 >容器中的operator[]注意事项网站首页技术交流
容器中的operator[]注意事项
首先看一张表格,支持operator[]
的容器包括string
、array
、vector
、deque
、map
、unordered_map
,顺序容器和关联容器的operator[]
不太一致。
string
中的operator[]
在pos < size()
时返回到位于指定位置pos
的字符的引用,或在pos == size()
时返回到拥有值CharT()
的字符(空字符)的引用。不进行边界检查。
如果 pos > size()
,那么行为未定义。
int main() {
std::string const e("Exemplar");
for (unsigned i = e.length() - 1; i != 0; i /= 2)
std::cout << e[i];
std::cout << '
';
const char* c = &e[0];
std::cout << c << '
'; // 作为 C 字符串打印
// 将 s 的最后一个字符改成 'y'
std::string s("Exemplar ");
s[s.size() - 1] = 'y';
std::cout << s << '
';
return 0;
}
运行结果
rmx
Exemplar
Exemplary
Array
中的operator[]
返回位于指定位置 pos
的元素的引用。不进行边界检查。
不同于std::map::operator[]
,此运算符决不插入新元素到容器。通过此运算符访问不存在的元素是未定义行为。
#include <iostream>
#include <map>
#include <string>
#include <string_view>
#include <vector>
#include <array>
using namespace std;
int main() {
std::array<int,4> numbers {2, 4, 6, 8};
std::cout << "Second element: " << numbers[1] << '
';
numbers[0] = 5;
std::cout << "All numbers:";
for (auto i : numbers) {
std::cout << ' ' << i;
}
std::cout << '
';
return 0;
}
运行结果
Second element: 4
All numbers: 5 4 6 8
vector
中的operator[]
返回位于指定位置 pos
的元素的引用。不进行边界检查。
#include <vector>
#include <iostream>
int main()
{
std::vector<int> numbers {2, 4, 6, 8};
std::cout << "Second element: " << numbers[1] << '
';
numbers[0] = 5;
std::cout << "All numbers:";
for (auto i : numbers) {
std::cout << ' ' << i;
}
std::cout << '
';
}
运行结果
Second element: 4
All numbers: 5 4 6 8
deque
中的operator[]
reference operator[]( size_type pos );
const_reference operator[]( size_type pos ) const;
返回位于指定位置 pos
的元素的引用。不进行边界检查。
#include <deque>
#include <iostream>
int main()
{
std::deque<int> numbers {2, 4, 6, 8};
std::cout << "Second element: " << numbers[1] << '
';
numbers[0] = 5;
std::cout << "All numbers:";
for (auto i : numbers) {
std::cout << ' ' << i;
}
std::cout << '
';
}
运行结果
Second element: 4
All numbers: 5 4 6 8
map
中的operator[]
T& operator[]( const Key& key );(1)
T& operator[]( Key&& key );(2) (C++11 起)
返回到映射到等于key
的键的值的引用,这种键不存在的情况下就会进行插入。
-
(`C++11 `前) 在键不存在的情况下插入` value_type(key, T())`
key_type
必须符合可复制构造 (CopyConstructible
) 的要求。mapped_type
必须符合可复制构造 (CopyConstructible
) 和 可默认构造 (DefaultConstructible
) 的要求。
如果进行插入,那么值初始化被映射值(对类类型为默认构造,否则为零初始化)并返回到它的引用。
c++11
起
- 在键不存在的情况下插入从
std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()
原位构造的value_type
对象。此函数等价于return this->try_emplace(key).first->second;
。 (C++17
起)
使用默认分配器时,这导致从 key 复制构造键,并值初始化被映射值。 - value_type 必须从
std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()
可就位构造(EmplaceConstructible)
。使用默认分配器时,这表明key_type
必须可复制构造 (CopyConstructible
) 而mapped_type
必须可默认构造 (DefaultConstructible
) 。
- 在键不存在的情况下插入从
std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()
原位构造的value_type
对象。此函数等价于return this->try_emplace(std::move(key)).first->second
; 。 (C++17
起)
使用默认分配器时,这导致从key
移动构造键,并值初始化被映射值。
value_type
必须从std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()
可就位构造 (EmplaceConstructible
) 。使用默认分配器时,这表明key_type
必须为可移动构造 (MoveConstructible
) 而mapped_type
必须为可默认构造 (DefaultConstructible
) 。
参数
key
- 要寻找的元素键
返回值
不存在拥有键key
的元素时返回到新元素被映射值的引用。否则返回到既存的关键等价于 key
的元素的被映射值的引用。
异常
如果任何操作抛出异常,那么插入无效果。
注解
出版的 C++11
和C++14
标准中,指定此函数要求mapped_type
为可默认插入 (DefaultInsertable
) 且 key_type
为可复制插入 (CopyInsertable
) 或可移动插入 (MoveInsertable
) 到 *this
。此规定有缺陷并为LWG
问题2469
所修复,而上面的描述合并了该问题的解决方案。
然而,已知一个实现(libc++
)通过两个分离的分配器construct()
调用构造key_type
和 mapped_type
对象,可认为如发布时的标准所要求,而非原位构造 value_type
对象。
operator[]
非 const
,因为它会在键不存在时插入键。如果此行为非所欲或容器为 const
,那么可以使用 at()
。
insert_or_assign()
返回的信息多于 operator[]
,而且不要求mapped_type
可默认构造。(C++17
起)
#include <iostream>
#include <map>
#include <string>
auto print = [](auto const comment, auto const& map) {
std::cout << comment << "{";
for (const auto& pair : map)
std::cout << "{" << pair.first << ": " << pair.second << "}";
std::cout << "}
";
};
int main() {
std::map<char, int> letter_counts{{'a', 27}, {'b', 3}, {'c', 1}};
print("letter_counts 初始状态下包含:", letter_counts);
letter_counts['b'] = 42; // 更新既存值
letter_counts['x'] = 9; // 插入新值
print("修改后它包含:", letter_counts);
// 统计每个单词的出现次数
// (首次调用 operator[] 会初始化计数为零)
std::map<std::string, int> word_map;
for (const auto& w : {"this", "sentence", "is", "not", "a", "sentence",
"this", "sentence", "is", "a", "hoax"})
++word_map[w];
word_map["that"]; // 插入对 {"that", 0}
for (const auto& [word, count] : word_map)
std::cout << "单词 '" << word << "' 出现 " << count << "次
";
}
运行结果,注意这个 word_map["that"]; // 插入对 {"that", 0}
是插入了一个map值
letter_counts 初始状态下包含:{{a: 27}{b: 3}{c: 1}}
修改后它包含:{{a: 27}{b: 42}{c: 1}{x: 9}}
单词 'a' 出现 2次
单词 'hoax' 出现 1次
单词 'is' 出现 2次
单词 'not' 出现 1次
单词 'sentence' 出现 3次
单词 'that' 出现 0次
单词 'this' 出现 2次
unordered_map
中的operator[]
T& operator[]( const Key& key );(1) (C++11 起)
T& operator[]( Key&& key );(2) (C++11 起)
返回到映射到等于 key
的键的值的引用,这种键不存在的情况下就会进行插入。
#include <iostream>
#include <string>
#include <unordered_map>
auto print = [](auto const comment, auto const& map)
{
std::cout << comment << "{";
for (const auto &pair : map)
std::cout << "{" << pair.first << ": " << pair.second << "}";
std::cout << "}
";
};
int main()
{
std::unordered_map<char, int> letter_counts{{'a', 27}, {'b', 3}, {'c', 1}};
print("letter_counts 初始状态下包含:", letter_counts);
letter_counts['b'] = 42; // 更新既存值
letter_counts['x'] = 9; // 插入新值
print("修改后它包含:", letter_counts);
// 统计每个单词的出现次数
// (首次调用 operator[] 会初始化计数为零)
std::unordered_map<std::string, int> word_map;
for (const auto& w : {"this", "sentence", "is", "not", "a", "sentence",
"this", "sentence", "is", "a", "hoax"})
++word_map[w];
word_map["that"]; // 插入对 {"that", 0}
for (const auto& [word, count] : word_map)
std::cout << "单词 '" << word << "' 出现 " << count << "次
";
}
运行结果
letter_counts 初始状态下包含:{{c: 1}{b: 3}{a: 27}}
修改后它包含:{{x: 9}{c: 1}{b: 42}{a: 27}}
单词 'a' 出现 2次
单词 'not' 出现 1次
单词 'is' 出现 2次
单词 'that' 出现 0次
单词 'hoax' 出现 1次
单词 'sentence' 出现 3次
单词 'this' 出现 2次