【问题标题】:why iterating over unordered_map with unique_ptr forces a pair with a key that is const?为什么用unique_ptr迭代unordered_map会强制一对键为const?
【发布时间】:2020-04-23 17:01:57
【问题描述】:

我发现了一个编译器错误,我无法理解为什么它只发生在带有 std::unique_ptr 的 std::map 上。

假设我们有以下未排序的地图对象和它的迭代代码:

std::unordered_map<uint32_t, std::shared_ptr<char>> map;

for (const std::pair<uint32_t, std::shared_ptr<char>>& item : map)
{
  // do something
}

编译得很好,但是如果我们使用唯一指针而不是共享指针,如下所示,那么我们会得到一个关于迭代对类型的编译器错误:

std::unordered_map<uint32_t, std::unique_ptr<char>> map;
for (const std::pair<uint32_t, std::unique_ptr<char>>& item : map)
{
  // do something 
}

error C2440: 'initializing': cannot convert from 'std::pair<const _Kty,_Ty>' to
 'const std::pair<uint32_t,std::unique_ptr<char,std::default_delete<_Ty>>>

出现此错误后,我们只需将“const”添加到键类型即可编译。

for (const std::pair<const uint32_t, std::unique_ptr<char>>& item : map)
                      ^^^
                      ||| 

为什么这个编译错误只发生在唯一指针上?

【问题讨论】:

    标签: for-loop unique-ptr unordered-map


    【解决方案1】:

    一位朋友给了我答案,我正在分享。

    答案在于允许编译器执行的隐式转换(复制)的概念。

    让我们看看下面这个简单的例子:

    const char  x = 4;
    const char& r = x;  // r is reference for x. Checking their addresses yields the same 
                        // address.
    
    const int&  ir = c; // implicit creation of object + conversion(copy). 
                        // ir is different type of x, therefor compiler does implicit
                        //  conversion(copy): it creates behind the scene an object of int, 
                        // convert x into this temporary object. The temporary int object is 
                        // then bound to the reference ir. Checking addresses of ir and x 
                        // yields different addresses because ir is reference of the temporary 
                        // object, not to x
    

    所以即使我们使用参考 - 计划只是指向现有对象, 我们实际上可能有一个对象构造+复制(如果类型不同并且它们之间存在转换)。

    我在问题中给出的循环中也发生了同样的情况:

    std::unordered_map<uint32_t, std::shared_ptr<char>> map;
    
    for (const std::pair<uint32_t, std::shared_ptr<char>>& item : map)
    {
      // do something
    }
    

    而地图持有的真实对象是

    std::pair<const uint32_t, std::shared_ptr<char>>
    

    循环使用不同类型的引用:

    std::pair<uint32_t, std::shared_ptr<char>> 
    

    因此,在幕后,对于每次迭代,都会隐式构造一个新的临时对象并完成转换的复制操作。

    不仅效率不高,而且值为unique_ptr时编译失败,因为转换做copy,unique_ptr不能复制。

    这就是为什么使用“自动”可以避免这样的错误,

    std::unordered_map<uint32_t, std::shared_ptr<char>> map;
    
    for (const auto& item : map)
    {
      // do something
    }
    

    我自己有时更喜欢玩和使用显式形式而不是“自动”来面对这些问题并学习:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-29
      • 2016-12-14
      • 1970-01-01
      • 2020-11-04
      相关资源
      最近更新 更多