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

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_mapconst,所以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::stringU2作为int.它实际上并不无论什么品种预选赛U1U2有,因为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&用于远程循环(甚至可能是一般的?),因为如果你不小心它可以避免副本.

  • 这错过了重点!看看你的(正确的)循环翻译!它说:const std :: pair <std :: string,int>&p(REFERENCE!)问题不是为什么std :: pair <std :: string,int> p = std :: pair <const std :: string,int> {}; 是允许的,这是一个简单的允许,因为它复制.当然可以将const复制到非const!问题是为什么std :: pair <std :: string,int>&p = std :: pair <const std :: string,int> {}; (参考)是允许的.答案是:不允许这样做.我错了什么? (7认同)