std::pair和map::value_type与unique_ptr之间的区别

4
在下面的代码示例中,我正在尝试使用std::unique_ptrs。我可以像预期的那样将unique_ptr添加到map中。让我惊讶的是,我不能将其作为std::pair的成员。示例中的注释行应该是我尝试构造与我的map::value_type相同类型的pair(我认为...)。我不确定为什么这不起作用。
提前感谢。
#include <iostream>
#include <memory>
#include <map>

#include <arpa/inet.h>

typedef std::map<uint32_t, std::unique_ptr<uint32_t>  > ntohl_map_type;
typedef std::map<uint32_t, uint32_t> u32_map_type;

void
u32_map()
{
    uint32_t key(0);
    uint32_t val(0);
    u32_map_type u32_map;

    u32_map.insert(u32_map_type::value_type(key, val));
    u32_map.insert(std::pair<uint32_t, uint32_t>(++key, ++val));

    std::cout << "u32_map: " << std::endl;
    for (auto &itr : u32_map) {
        std::cout << itr.first << " = " << itr.second << "\n";
    }
    std::cout << std::endl;
}

void
uptr_map()
{
    uint32_t key(9);
    std::unique_ptr<uint32_t> u32_uptr1(new uint32_t(ntohl(key)));
    ntohl_map_type ntohl_map;

    ntohl_map.insert(ntohl_map_type::value_type(key, std::move(u32_uptr1)));

    ++key;
    std::unique_ptr<uint32_t> u32_uptr2(new uint32_t(ntohl(key)));

    // It seems odd these don't work....
    //foo = std::pair<uint32_t, std::unique_ptr<uint32_t>(key, std::move(u32_uptr2));
    //ntohl_map.insert(std::pair<uint32_t, std::unique_ptr<uint32_t>(key, std::move(u32_uptr2)));

    std::cout << "uptr_map: " << std::endl;
    for (auto &itr : ntohl_map) {
        std::cout << itr.first << " = " << *itr.second << "\n";
    }
}

int
main()
{
    u32_map();
    uptr_map();

    return 0;
}

编辑: 刚意识到编译器错误可能是有用的:

error: no matching constructor for initialization of 'std::unique_ptr<uint32_t>'
  ...const, std::unique_ptr<uint32_t>(key, std::move(u32_uptr2)));
        ^                         ~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/c++/v1/memory:2554:31: note: candidate constructor not viable: no known conversionfrom 'uint32_t' (aka 'unsigned int') to 'pointer' (aka 'unsigned int *') for 1st argument; take the address of the argument with &
_LIBCPP_INLINE_VISIBILITY unique_ptr(pointer __p, typename conditional<
                          ^
/usr/bin/../lib/c++/v1/memory:2561:31: note: candidate constructor not viable: no known conversion from 'uint32_t' (aka 'unsigned int') to 'pointer' (aka 'unsigned int *') for 1st argument; take the address of the argument with &
_LIBCPP_INLINE_VISIBILITY unique_ptr(pointer __p, typename...
1个回答

6
你忘记了 map 的关键字是常量(这是为了防止你有意或无意地搞乱关联容器的内部排序):
ntohl_map.insert(
    std::pair<uint32_t const, std::unique_ptr<uint32_t>>(
//                     ^^^^^
        key, std::move(u32_uptr2)));

为了避免犯错,你可以这样做:
ntohl_map.insert(ntohl_map_type::value_type(key, std::move(u32_uptr2)));

您提供的原始代码中调用insert()无法编译,这是因为您提供的pair类型与insert()接受的pair类型不同(因为有const限定符),需要进行转换,结果尝试从您提供的pair复制构造临时pair。
复制构造pair意味着复制构造其元素,由于std::unique_ptr不可复制构造,您的程序无法编译通过。
使用map<uint32_t, uint32_t>的函数可以编译,原因是uint32_t显然是可复制构造的。
此外,请注意,自C++11以来,std::map具有emplace()成员函数(某些实现尚未提供),允许就地构造其元素。
 ntohl_map.emplace(key, std::move(u32_uptr2));

我认为那不是问题所在。首先,您会注意到在u32_map函数中我不需要这样做。此外,如果您注意到,我已经有了最后一个灯光(使用value_type),我只是认为它们是等效的语句,我不明白为什么一个可以工作而另一个不能。 - user2466803
@user2466803:我在上次回答的最后一次编辑中试图解释这个问题。请检查更新后的答案。另外,请尝试我的建议-你会看到你的代码编译通过。 - Andy Prowl
啊,复制构造确实有道理。虽然你的解决方案对我来说仍然无法编译。为了避免复制构造,我不得不std::move temp std::pair,就像这样:ntohl_map.insert(std::move(std::pair<uint32_t, std::unique_ptr<uint32_t>>(key, std::move(u32_uptr2)))); - user2466803
但是你好像还忘了 const。在发布代码之前,我已经尝试过并且编译成功了(请参见此实时示例)。 - Andy Prowl
哎呀……现在我更加困惑了 :) 看起来 const 甚至都不需要。我最终将你的代码行放在我的下面,注意到了区别。我 std::pair 缺少一个 '>'。 - user2466803

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接