max*_*max 19 c++ templates const c++11
为什么C++允许编译以下代码?
std::unordered_map<std::string, int> m;
// ...
for (const std::pair<std::string, int>& p: m)
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
根据Scott Meyers的Effective Modern C++(p.40-41):
[...] a的关键部分
std::unordered_map
是const
,所以std::pair
哈希表中的类型(它是什么std::unordered_map
)不是std::pair<std::string, int>
,它是std::pair <const std::string, int>
.但这不是p
上面循环中为变量声明的类型.因此,编译器将努力找到一种方法将std::pair<const std::string, int>
对象(即哈希表中的内容)转换为std::pair<std::string, int>
对象(声明的类型p
).它们将p
通过复制每个对象来创建一个想要绑定的类型的临时对象m
,然后将引用p绑定到该临时对象.在每次循环迭代结束时,临时对象将被销毁.如果你编写了这个循环,你可能会对这种行为感到惊讶,因为你几乎肯定打算简单地将引用绑定p
到每个元素中m
.
允许这种隐式转换有什么好处?是否有一些常见的用例,开发人员期望/更喜欢这种隐式转换(而不是获得编译器错误)?
Rak*_*111 22
符合标准的编译器将"看到"for循环,如下所示:
auto&& __range = m;
for (auto __begin = std::begin(m), __end = std::end(m); __begin != __end; ++__begin) {
const std::pair<std::string, int>& p = *__begin;
//loop_statement
}
Run Code Online (Sandbox Code Playgroud)
这基本上归结了你的问题为什么允许以下代码:
std::pair<std::string, int> p = std::pair<const std::string, int>{};
Run Code Online (Sandbox Code Playgroud)
请注意,我放弃了const&
部分p
,因为它不相关.转换是一样的,唯一的区别是临时绑定到引用而不是被复制.
如果您想知道为什么OP的片段不能与非const引用一起使用,那么转换就是原因所在.转换的结果是一个临时对象,因为对临时对象的任何更改都是无用的(它的生命周期没有被扩展,因此它会在之后被销毁),所以语言不允许它.
这是允许的,因为它std::pair
有一个启用此转换的构造函数.
template< class U1, class U2 >
pair( const pair<U1, U2>& p );
Run Code Online (Sandbox Code Playgroud)
在你的情况,U1
推导作为const std::string
和U2
作为int
.它实际上并不无论什么品种预选赛U1
和U2
有,因为p
的元素被复制.
其好处与允许的原因相同:
const int zero{};
int zero2 = zero;
Run Code Online (Sandbox Code Playgroud)
例如,请考虑以下非现实示例:
struct {
std::pair<int, int> pos;
} player;
std::pair<const int, const int> treasure{1, 2}; // position of treasure
player.pos = treasure; // ok
Run Code Online (Sandbox Code Playgroud)
现在,当你说什么如果,这种转换是出于某种原因不能容许.程序员必须做什么?
player.pos.first = treasure.first;
player.pos.second = treasure.second;
Run Code Online (Sandbox Code Playgroud)
如果这也是不允许的话,那么上面的零的情况也是不允许的,这实际上没有意义,因为你正在复制zero
,所以你是否可以修改它并不重要,因为那是一个完全不同的操作.
如果这是允许的,那么为什么会player.pos = treasure;
被禁止,因为它唯一能做的就是复制?如上所述,您是否可以更改元素并不重要treasure
,因为您只是复制它们.
这也是为什么你应该使用auto&&
或const auto&
用于远程循环(甚至可能是一般的?),因为如果你不小心它可以避免副本.