1

How to write user defined copy-constructor of a class/struct with array of atomic variables? For example the following code does not compile

struct foo
{    
     std::array<std::atomic_int,3> a;
     foo() = default;
     foo(const int& i, const int& j, const int& k):a{{i,j,k}}{}
}

for the reason that "error: declared here __atomic_base(const __atomic_base&) = delete;" i.e. this is deleted in the definition of atomic type. Had it been non atomic it would have been done.

Is there some way to do this?

I have gone through the discussions here C++ - How to initialise an array of atomics? .

Community
  • 1
  • 1
BapiC
  • 11
  • 2
  • 1
    Note: `foo(const int& i, const int& j, const int& k)` is not a *copy constructor*. If you want to write that constructor, you need *even more braces* (that often helps ;) `foo(const int& i, const int& j, const int& k):a{{{i},{j},{k}}} {}` That has to do with aggregate-initialization, which invokes copy-initialization of the aggregate members. – dyp Jan 15 '14 at 17:09
  • Hi @dyp, thanks for pointing out that. The problem is solved now as foo(const int& i, const int& j, const int & k){a[0].store(int(i)); a[1].store(int(j)); a[2].store(int(k));}. Thanks! – BapiC Jan 15 '14 at 17:49

2 Answers2

1

The std::atomic copy constructor is deleted, because there is no way to atomically copy the value from one object to another:

std::atomic<int> a, b;
a = b; //  use of deleted function 'std::atomic<int>& std::atomic<int>::operator=(const std::atomic<int>&)'

You can, however, read the value from one atomic and then store what you read into another, you just need some uncomfortable syntax to make it explicit that you are not expecting an atomic copy:

std::atomic<int> a, b;
a = b.load();

You can copy an array of atomics this way with a for loop:

std::array<std::atomic<int>, 3> a, b;
for (const auto& i : b) {
  a = i.load();
}

or - to avoid the explicit loop - a transform:

std::array<std::atomic<int>, 3> a, b;
std::transform(begin(b), end(b), begin(a),
               [](int i){return i;});

so I would implement your copy constructor as:

foo(const foo& other) {
    std::transform(begin(other.a), end(other.a), begin(a),
                   [](int i){return i;});
}
Casey
  • 41,449
  • 7
  • 95
  • 125
  • @dyp Good idea for the general case, but it would make the lambda for the `transform`s a bit weird. I would not write `std::transform(begin(b), end(b), begin(a), [](decltype(*b.cbegin()) i){return i.load();})` unless genericity was absolutely critical, although I might use `[](decltype(other.a[0]) i){return i.load();}` in the copy constructor for code I write that is not for an SO answer. `[](const auto& foo){return foo.load()}` would be nice, of course - I assume the OP doesn't have a C++14 compiler ;) – Casey Jan 15 '14 at 17:33
  • @dyp tsk, tsk - copying! Sorry I stomped on you with my edit. – Casey Jan 15 '14 at 17:36
  • :D ok... was confused for a second – dyp Jan 15 '14 at 17:37
  • Hi guys Casey and @dyp, thanks indeed for your nice answer and informed discussion. My purpose was to write the user-defined copy-constructor as foo(const int& i, const int& j, const int & k) and indeed taking clue from yours discussion I now have it as foo(const int& i, const int& j, const int & k){a[0].store(int(i)); a[1].store(int(j)); a[2].store(int(k));} and it works perfectly. I can not upvote your answers as I do not have that many reputations as it flashes, but, I indeed want to do that. dyp please upvote this discussion. – BapiC Jan 15 '14 at 17:42
0

Just define the copy constructor manually, and copy the array element by element in the body of the constructor (if it is supposed to be copied). Just be aware that this is not atomic.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • Hi @SebastianRedl, I tried that and it does not work precisely for the reason which you have already mentioned as a caution. My purpose is to write a constructor for this class but yes it does not work even if **a** is not an array. – BapiC Jan 15 '14 at 16:22