为什么 C++ 允许编译以下代码?
std::unordered_map<std::string, int> m;
// ...
for (const std::pair<std::string, int>& p: m)
{
// ...
}
根据Scott Meyers' Effective Modern C++ (第 40-41 页):
[...] the key part of a
std::unordered_map
isconst
, so the type ofstd::pair
in the hash table (which is what astd::unordered_map
is) isn’tstd::pair<std::string, int>
, it'sstd::pair <const std::string, int>
. But that's not the type declared for the variablep
in the loop above. As a result, compilers will strive to find a way to convertstd::pair<const std::string, int>
objects (i.e., what’s in the hash table) tostd::pair<std::string, int>
objects (the declared type forp
). They’ll succeed by creating a temporary object of the type thatp
wants to bind to by copying each object inm
, then binding the reference p to that temporary object. At the end of each loop iteration, the temporary object will be destroyed. If you wrote this loop, you'd likely be surprised by this behavior, because you'd almost certainly intend to simply bind the referencep
to each element inm
.
允许这种隐式转换有什么好处?是否存在一些开发人员期望/更喜欢这种隐式转换(而不是编译器错误)的常见用例?
最佳答案
符合标准的编译器会“看到”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
}
这基本上将您的问题归结为为什么允许使用以下代码:
std::pair<std::string, int> p = std::pair<const std::string, int>{};
请注意,我删除了 p
的 const&
部分,因为它不相关。转换是一样的,唯一的区别是临时绑定(bind)的是引用而不是被复制。
如果您想知道为什么 OP 的代码段不适用于非常量引用,那么转换就是原因。转换的结果是一个临时对象,并且因为对临时对象的任何更改都是无用的(它的生命周期没有延长,所以它会立即被销毁),所以语言不允许这样做。
这是允许的,因为 std::pair
有一个构造函数可以实现这种转换。
template< class U1, class U2 >
pair( const pair<U1, U2>& p );
在您的情况下,U1
被推断为 const std::string
,U2
被推断为 int
。 cv 限定符 U1
和 U2
有什么实际上并不重要,因为 p
的元素会被复制。
好处与允许这样做的原因相同:
const int zero{};
int zero2 = zero;
例如,考虑以下不现实的例子:
struct {
std::pair<int, int> pos;
} player;
std::pair<const int, const int> treasure{1, 2}; // position of treasure
player.pos = treasure; // ok
现在,如您所说,如果出于某种原因不允许允许这种转换。程序员需要做什么?
player.pos.first = treasure.first;
player.pos.second = treasure.second;
如果这也被禁止,那么上面带零的情况也将被禁止,这没有任何意义,因为你正在复制 zero
, 所以能不能修改都无所谓,因为那是完全不同的操作。
如果这是允许的,那么为什么 player.pos = Treasure;
会被禁止,因为它唯一的作用就是复制?和上面一样,你是否可以改变 treasure
的元素并不重要,因为你只是在复制它们。
这也是为什么你应该对范围循环使用 auto&&
或 const auto&
的原因(甚至可能是一般情况下?)因为如果你不这样做,它可以避免复制小心。
关于c++ - 为什么允许从 const 到非 const 的隐式转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45910102/