Thread-safe LRU cache Please review my code. I am trying to implement a thread-safe LRU cache using std::list and std::unordered_map. This is a header-only template class which you can use to keep an object cache.

The constructor takes a size as an input parameter and creates a cache of a specified size. Items will be removed based on the least recently used from the cache if the cache goes above the size.

#ifndef LRUCache_h__
#define LRUCache_h__

#pragma once
#include &lt;vector&gt;
#include &lt;list&gt;
#include &lt;unordered_map&gt;
#include &lt;shared_mutex&gt;
#include &lt;iostream&gt;

namespace objcache {

template &lt;class T&gt;
class LRUCache
{    
    struct _node {
        int _key;
        T    _value;
    };

    // use map of key to iterator of LRU list
    using ListIterator = typename std::list&lt;_node&gt;::const_iterator;
    using CacheMap = std::unordered_map&lt;int, ListIterator&gt;;

    size_t                m_cacheSize;
    std::list&lt;_node&gt;    m_lruList;
    CacheMap            m_lruMap;
    std::mutex            m_lock;            // reader writer lock, multiple getters are allowed

public:

    /***
     * Method:        objcache::LRUCache::LRUCache
     * Access:        public 
     * Description: This creates a cache of given size. 
     */
    LRUCache(size_t size) :
        m_cacheSize{ size },
        m_lruList{},
        m_lruMap{},
        m_lock{}
    {
        //m_lruList.resize(m_cacheSize, 0);
        m_lruMap.reserve(m_cacheSize);
    }

    /***
     * Method:    objcache::LRUCache::set
     * Access:    public      
     * Description : This API updates or sets the value val to key. 
     */
    void Set(int key, const T &amp;val) noexcept
    {
        std::lock_guard&lt;std::mutex&gt; guard(m_lock);

        if (auto mapIter = m_lruMap.find(key); mapIter != m_lruMap.end()) {
            //key already exists, just update the queue
            m_lruList.splice(m_lruList.begin(), m_lruList, mapIter-&gt;second);
        }
        else {
            // new item in the cache.
            m_lruList.push_front({ key, val });
            m_lruMap.insert(std::make_pair(key, m_lruList.begin()));
        }

        //remove least recently used element if cache is full. 
        if (m_lruList.size() &gt; m_cacheSize) {
            m_lruMap.erase(m_lruList.back()._key);
            m_lruList.pop_back();
        }
    }

    /***
     * Method:        objcache::LRUCache::get
     * Access:        public 
     * Description: This API gets the value of key if it exists in cache,
     *                otherwise, just returns 0
     */
    T Get(int key) noexcept
    {
        std::lock_guard&lt;std::mutex&gt; gaurd(m_lock);
        T val = 0;
        if (auto key_iter = m_lruMap.find(key); key_iter != m_lruMap.end()) {
            m_lruList.splice(m_lruList.begin(), m_lruList, key_iter-&gt;second);
            val = (key_iter-&gt;second)-&gt;_value;
        }
        return val;
    }

    /***
     * Method:    objcache::LRUCache::printCache
     * Access:    public 
     * Description : Prints the keys of the cache one-by-one
     */
    void PrintCache() noexcept
    {
        std::lock_guard&lt;std::mutex&gt; guard(m_lock);
        for (const auto &amp;node : m_lruList)
            std::cout &lt;&lt; node._key &lt;&lt; std::endl;
    }

};

} // namespace objcache

#endif // LRUCache_h__
Not repeating what 1201ProgramAlarm reported.

std::mutex    m_lock;   // reader writer lock, multiple getters are allowed

I see a regular exclusive lock, not a reader/writer lock.
std::lock_guard&lt;std::mutex&gt; gaurd(m_lock);

Not taking advantage of class template argument deduction?  This is a case where I really want it.  (If not, use an alias for the lock type so it can be easily changed.)
std::lock_guard guard(m_lock);


    m_lruList{},
    m_lruMap{},
    m_lock{}

Just adds noise — is that a thing someone actually recommends?  I would say don’t list all the things that are taken care of on their own.
   for (const auto &amp;node : m_lruList)
        std::cout &lt;&lt; node._key &lt;&lt; std::endl;

The style in C++ is to put the * or &amp; with the type, not the identifier.  This is called out specifically near the beginning of Stroustrup’s first book, and is an intentional difference from C style.
Some people criticize the leading underscore of identifiers as a style.  I point out that it is easy to clash with compiler-supplied identifiers and you must remember to never use a capital letter after the score, so it is a hazard.

Update
#ifndef LRUCache_h__

Any name containing a double underscore is reserved for use by the implementation.

/***
 * Method:        objcache::LRUCache::LRUCache
 * Access:        public 
 * Description: This creates a cache of given size. 
 */

Banner comments like this just get in the way of reading the code.  Repeating the function name in a comment just before its definition does what exactly?  (Oh, and a point of nomenclature:  C++ does not have “methods”.)  Nobody comments each function as being public or private.  For the description, don’t prefix it with “Description:”; just describe it.
Since I can see name of the argument is size and see how it’s used on the on the following line
LRUCache(size_t size) :
    m_cacheSize{ size },

a comment saying
// This creates a cache of given size

is useless.  It only repeats what is in the signature:  A constructor creates an instance of this class, and this one takes a size.
See (⧺P.1).  You might also find this useful (Comments starts on page 17; material originally published in 1992).