Состояние значения для вставки - PullRequest
3 голосов
/ 11 апреля 2019

Возможно ли, что значение, переданное для вставки, остается в , переведенном из состояния после вставки перемещения, если insert возвращает false?

#include <memory>
#include <map>

#include <cassert>

struct less
{
    template< typename T >
    bool operator () (const std::shared_ptr<T> & lhs, const std::shared_ptr<T> & rhs) const
    {
        return *lhs < *rhs;
    }
};

int main() {
    using key_type = int;
    using value_type = int;
    using map_type = std::map<std::shared_ptr<key_type>, std::shared_ptr<value_type>, less>;
    map_type m;
    auto p = typename map_type::value_type{std::make_shared<key_type>(1), std::make_shared<value_type>(1)};
    if (!m.insert(p).second) {
        assert(false);
    }
    assert(p.first);
    assert(p.second);
    if (m.insert(std::move(p)).second) {
        assert(false);
    }
    assert(p.first);
    assert(p.second);
}

Поведениеопределена реализация двух последних утверждений?

1 Ответ

3 голосов
/ 11 апреля 2019

С [map.modifiers / 2] на std::map::insert, у нас есть

template<class P>  
pair<iterator, bool> insert(P&& x);

[...]

Эффекты: первая форма эквивалентна return emplace(std::forward<P>(x)).

Так что это в std::map::emplace ... из [associative.reqmts / 8] (выделено мной):

a_­uniq.​emplace(args)

Эффекты: Вставляет value_­type объект t, созданный с std::forward<​Args​>(​args)... , тогда и только тогда, когда в контейнере нет элемента с ключом, эквивалентным ключу t.

Следовательно, конструкция не имеет место, если в контейнере уже есть объект, связанный с эквивалентным ключом.


Давайте проверим с <map> из реализации Llvm. Далее я удалил некоторые части кода, чтобы сделать его более читабельным. Сначала std::map::insert делает это:

template <class _Pp, /* some SFINAE... */>
/* symbol visibility ... */
pair<iterator, bool> insert(_Pp&& __p)
    {return __tree_.__insert_unique(_VSTD::forward<_Pp>(__p));}

Давайте перейдем к __tree::insert_unique, затем:

pair<iterator, bool> __insert_unique(__container_value_type&& __v) {
    return __emplace_unique_key_args(_NodeTypes::__get_key(__v), _VSTD::move(__v));
}

Все еще не там ... но в __tree::emplace_unique_key_args приходит:

/* Template, template, template... return value... template */
__tree</* ... */>::__emplace_unique_key_args(_Key const& __k, _Args& __args)
{
    __parent_pointer __parent;
    __node_base_pointer& __child = __find_equal(__parent, __k);
    __node_pointer __r = static_cast<__node_pointer>(__child);
    bool __inserted = false;
    if (__child == nullptr)
    {
        /* Some legacy dispatch for C++03... */

        // THIS IS IT:
        __node_holder __h = __construct_node(_VSTD::forward<_Args>(__args)...);
        __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
        __r = __h.release();
        __inserted = true;
    }
    return pair<iterator, bool>(iterator(__r), __inserted);
}

Я думаю, нам не нужно заглядывать в __find_equal(__parent, __k), чтобы понять, что __child == nullptr - это условие, которое запускает фактическую вставку. В этой ветке вызов __construct_node переадресует аргументы, которые украдут ресурсы, которыми управляет передаваемый вами std::shared_ptr<int>. Другая ветка просто оставит аргументы нетронутыми .

...