Double checked locking with shared_ptr I have an unordered_map&lt;int, std::shared_ptr&lt;foo&gt;&gt; into which I want to lazily create the instances of foo based on callbacks. These callbacks would come from potentially a pool of threads, so to handle this, at the start I create the map and insert empty shared_ptr&lt;&gt;s into the map for all possible indexes. Now I have the code below which is designed to create the foo instance based on demand.

Is my implementation of double checked locking with shared_ptr&lt;&gt; correct?

void Publish(const bar&amp; data)  {
    auto id = data.get_id();
    auto it = foos_.find(id);
    if (it == foos_.end()) {
      // throw
    }
    // Check if this is present
    auto sp = std::atomic_load_explicit(&amp;(it-&gt;second), std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);
    if (!sp) {
      // Create the instance of the foo for this id
      std::lock_guard&lt;std::mutex&gt; lock(mutex_);
      (void) lock;
      // Check again
      sp = std::atomic_load_explicit(&amp;(it-&gt;second), std::memory_order_relaxed);
      if (!sp) {
        // Create new foo
        sp = std::make_shared&lt;foo&gt;(...);
        std::atomic_thread_fence(std::memory_order_release);
        std::atomic_store_explicit(&amp;(it-&gt;second), sp, std::memory_order_relaxed);
      } // Someone else created it...
    }

    // Push the data in
    sp-&gt;Publish(data);
  }
Your implementation of Double Checked Locking is semantically correct; the acquire and release fences provide minimal necessary ordering.

I am wondering why you are using standalone fences, which is almost never necessary..
It is more common to use memory ordering directly on atomic operations:

auto sp = std::atomic_load_explicit(&amp;(it-&gt;second), std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);

...

std::atomic_thread_fence(std::memory_order_release);
std::atomic_store_explicit(&amp;(it-&gt;second), sp, std::memory_order_relaxed);


Can be replaced with:

auto sp = std::atomic_load_explicit(&amp;(it-&gt;second), std::memory_order_acquire);

...

std::atomic_store_explicit(&amp;(it-&gt;second), sp, std::memory_order_release);


Furthermore, I understand why you are using (void) lock;, but I would not use that line just to suppress a compiler warning.
Also I am surprised about that warning since a modern compiler should know that RAII objects have a function. Are you sure there is a warning or is that an assumption ?