std :: unique_ptr с помощью std :: map - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть std::map, где ключ std::shared_ptr<Foo>, а значение std::unique_ptr<Bar>, где Foo и Bar очень разные классы из сторонней библиотеки.Я использую этот объект std::map в качестве кэша в памяти.

Мне интересно, каким будет лучший способ вставки новой записи в эту карту, а затем возвращен из метода, учитывая, что Bar переданный в std::unique_ptr уже будет построен?

В настоящее время у меня есть следующее:

class SomeClass
{
public:

    const Bar* TryGetBarValue(std::shared_ptr<Foo> foo)
    {
        auto it = _cache.find(foo);

        if(it == _cache.end())
        {
            Bar bar = ThirdPartLibrary::CreateBar();
            _cache.emplace(foo, std::make_unique<Bar>(bar));
            return _cache.rbegin()->second.get();
        }

        //return result as raw ptr from unique_ptr
        return it->second.get();
    }

private:
    std::map<std::shared_ptr<Foo>, std::unique_ptr<Bar>> _cache;
}

РЕДАКТИРОВАТЬ

Благодаря ответупредоставленный Квентином, теперь это моя реализация:

class SomeClass
{
public:

    const Bar* TryGetBarValue(std::shared_ptr<Foo> foo)
    {
        auto it = _cachedImages.find(texture);

        if (it != _cachedImages.end())
        {
            return it->second.get();
        }

        return _cachedImages.emplace(
                std::move(texture), 
                std::make_unique<sf::Image>(texture->copyToImage())
            ).first->second.get(); 
        }

private:
    std::map<std::shared_ptr<Foo>, std::unique_ptr<Bar>> _cache;
}

Спасибо за вашу помощь!

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Вы пометили это как c ++ 14, но для потомства я добавлю версию C ++ 17:

const Bar* TryGetBarValue(std::shared_ptr<Foo> foo)
{
    struct DelayedBar
    {
        operator std::unique_ptr<Bar>() const { return std::make_unique<Bar>(thirdpartyLibrary::CreateBar()); }
    };
    return _cache.try_emplace(std::move(foo), DelayedBar()).first->second.get();
}

Функция try_emplace будет использовать свои аргументы, если карта неуже содержат этот ключ.Если ключ уже существует, объект не создается.В любом случае возвращается итератор этой пары ключ / значение.Эта функция избегает двойного поиска, когда вы делаете find -> emplace/insert.

В нашем случае мы не можем просто передать аргументы try_emplace, поэтому я попытался быть умным взадержка строительства объекта с использованием этого DelayedBar класса.Он вызывает CreateNewBar только при попытке привести к std::unique_ptr<Bar>, что происходит только тогда, когда try_emplace пытается построить объект.

Я скомпилировал это с GCC 8.2, Clang 7.0.0 и MSVC 19.16(все через Compiler Explorer), и все в порядке.

0 голосов
/ 22 февраля 2019

return _cache.rbegin()->second.get(); делает не то, что вы хотите, поскольку std::map не добавляет элементы, а сортирует их.Однако emplace возвращает итератор к тому, что он только что вставил, поэтому вам нужно только:

return _cache.emplace(foo, std::make_unique<Bar>(bar))->first->second.get();

Или даже, поскольку вам на самом деле не нужно хранить и копировать Bar, и вы также можетежертва foo:

return _cache.emplace(
    std::move(foo),
    std::make_unique<Bar>(ThirdPartLibrary::CreateBar())
)->first->second.get();

Я бы лично перевернул условие (it == _cache.end()), чтобы сделать его досрочным возвращением, но это всего лишь вопрос вкуса.

В противном случае, что выхорошо выглядит для меня.

...