Search code examples
c++initializer-listexplicitstdatomic

can't compile struct with std::atomic member with explicit instantiation


I am exploring the use of std::atomic in a struct across translation units and have run into a constructor compile problem. When I try to use explicit instantiation, the compiler says they don't match. How do I match up the explicit instantiation and the A constructor?

#include <string>
#include <atomic>
#include <map>

struct A
{
  A( std::string strArg, bool onOffArg ) // added constuctor after compiler complained it couldn't find one that matched
    : str { strArg }, onOff { onOffArg } {}
  ~A() {}

  std::string str {};
  std::atomic< bool > onOff { false }; // (see Edit1, Remy Lebeau). error C2440: 'initializing': cannot convert from 'initializer list' to 'std::map<int,A,std::less<int>,std::allocator<std::pair<const int,A>>>', 'No constructor could take the source type, or constructor overload resolution was ambiguous'
};
A( const A& oldA ) // (see Edit2, Eugene)
{
  str = oldA.str;
  onOff.store( oldA.onOff.load() );
}


int main()
{
  std::map< int, A > aMap
  {
    { 1, { "One", false } } // assuming inner braces are a match for A ctor
  };
}

Edit1: Fixed atomic constructor.

Edit2: Copy ctor was missing (see reply to Eugene comment). Also, atomic's store and load need to be used instead of assign in copy ctor.


Solution

  • The immediate problem is that struct A is non-copyable and non-movable: its auto-generated copy and move ctors are deleted because std::atomic is non-copyable and non-movable. A map with a non-movable value type can be created, but many operations on it are disabled, including the construction from the initializer list.

    The underlying design problem is your decision to use std::atomicflag as part of a struct. In a multi-threaded environment, you probably want to synchronize updates to all struct members. In this case, it is better to include a non-atomic flag in the struct, and protect the operations modifying the map with a mutex.

    So, your struct could be just

    struct A
    {
       std::string str;
       bool onOff;
    };
    

    and you make a wrapper class containing both std::map< int, A > and a mutex, with non-const methods containing locks on the mutex.