c++ - 为什么允许从 const 到非 const 的隐式转换?

标签 c++ c++11 templates constants

为什么 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 is const, so the type of std::pair in the hash table (which is what a std::unordered_map is) isn’t std::pair<std::string, int>, it's std::pair <const std::string, int>. But that's not the type declared for the variable p in the loop above. As a result, compilers will strive to find a way to convert std::pair<const std::string, int> objects (i.e., what’s in the hash table) to std::pair<std::string, int> objects (the declared type for p). They’ll succeed by creating a temporary object of the type that p wants to bind to by copying each object in m, 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 reference p to each element in m.

允许这种隐式转换有什么好处?是否存在一些开发人员期望/更喜欢这种隐式转换(而不是编译器错误)的常见用例?

最佳答案

符合标准的编译器会“看到”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>{};

请注意,我删除了 pconst& 部分,因为它不相关。转换是一样的,唯一的区别是临时绑定(bind)的是引用而不是被复制。

如果您想知道为什么 OP 的代码段不适用于非常量引用,那么转换就是原因。转换的结果是一个临时对象,并且因为对临时对象的任何更改都是无用的(它的生命周期没有延长,所以它会立即被销毁),所以语言不允许这样做。

这是允许的,因为 std::pair 有一个构造函数可以实现这种转换。

template< class U1, class U2 >
pair( const pair<U1, U2>& p );

在您的情况下,U1 被推断为 const std::stringU2 被推断为 int。 cv 限定符 U1U2 有什么实际上并不重要,因为 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/

相关文章:

c++ - 返回一个字符指针(内存泄漏)

javascript - 为什么 Emscripten 会为添加到分配的 Float32Array 中的某些数字抛出错误?

c++ - 将没有定义的 static const int 的地址传递给模板是否合法?

c++ - 模板参数中的T&和T&&有什么区别?

C++ Lambda : Difference between "mutable" and capture-by-reference

c++ - 无法执行从二维数组到另一个二维数组的 strcpy

c++ - 如何使用快速傅立叶变换计算数组元素的乘积之和?

c++ - 使用并行线程的矩阵乘法

c++ - 编译时检查#pragma pack 的使用情况

c++ - 根据调用的构造函数更改成员数