如何避免将const-ref返回缓存中的临时性

How to avoid returning const-ref to a temporary in a cache

本文关键字:临时性 缓存 返回 何避免 const-ref      更新时间:2023-10-16

我有一个简单的对象缓存:

class ObjectCache
{
public:
    ObjectCache() {}
    const Object& object(const std::string &key) const
    {
        auto it = cache_.find(key);
        if (it != cache_.end())
        {
            return it->second;
        }
        return Object(); // const-ref to temporary
    }
    void insert(const std::string &key, const Object &object)
    {
        cache_[key] = object;
    }
private:
    std:map<std::string, Object> cache_;
};

返回类型是从缓存中检索的是const Ref。

但是,在没有找到密钥的情况下,返回了临时的const Ref,并导致调用代码的不确定行为。

如何解决将const ref返回临时的问题?

我有一些想法:

  • 插入和返回指针(缓存获得所有权),未找到的nullptr
  • 提供objectCache ::包含
  • 维护静态对象或空对象成员,并在找不到时返回引用

理想的解决方案是维护当前的缓存,但将指针返回给参考:

class ObjectCache
{
public:
    ObjectCache() {}
    const Object* object(const std::string &key) const
    {
        auto it = cache_.find(key);
        if (it != cache_.end())
        {
            return &(it->second);
        }
        return nullptr;
    }
    void insert(const std::string &key, const Object &object)
    {
        cache_[key] = object;
    }
private:
    std:map<std::string, Object> cache_;
};

这具有避免在堆和内存管理上创建对象的额外好处,但允许呼叫代码与未找到的NULLPTR一起使用。

您可以提供允许呼叫者表达所需行为的接口:

#include <map>
#include <string>
#include <stdexcept>
#include <boost/optional.hpp>
struct Object {};
struct NoObject : std::logic_error
{
    using std::logic_error::logic_error;
};
class ObjectCache
{
public:
    ObjectCache() {}
    /// Require an object for the corresponding key
    /// @param key
    /// @exception NoObject if the key does not represent an object
    ///  in cache
    const Object& require(const std::string &key) const
    {
        auto it = cache_.find(key);
        if (it != cache_.end())
        {
            return it->second;
        }
        throw NoObject(key);
    }
    /// return the object corresponding to key if it exists
    /// If not, call the factory function, store the result and return
    /// the corresponding object
    template<class Factory>
    const Object& acquire(const std::string &key, Factory&& factory) 
    {
        auto it = cache_.find(key);
        if (it == cache_.end())
        {
            it = cache_.emplace(key, factory(key)).first;
        }
        return it->second;
    }

    /// Return the object corresponding to key if it exists.    
    boost::optional<const Object&> query(const std::string &key) const
    {
        auto it = cache_.find(key);
        if (it != cache_.end())
        {
            return it->second;
        }
        else
        {
            return {};
        }
    }
    void insert(const std::string &key, const Object &object)
    {
        cache_[key] = object;
    }
private:
    std::map<std::string, Object> cache_;
};
int main()
{
    ObjectCache cache;
    // fetch existing or create
    auto& x = cache.acquire("foo", [](std::string const& key){
        return Object();
    });
    // fetch existing or exception
    auto& y = cache.require("foo");
    // fetch existing if exists
    if(auto z = cache.query("foo"))
    {
        auto&& zz = z.get();
    }
}