5

When compiling the following piece of code (gcc-4.8, --std=c++11):

#include <atomic>
#include <utility>
#include <cstdint>

struct X {
    std::atomic<std::pair<uint32_t, uint32_t>> A;
};

I get the following compilation error:

/usr/local/include/c++/4.8.2/atomic:167:7: error: function
 'std::atomic<_Tp>::atomic() [with _Tp = std::pair<unsigned int, unsigned int>]'
 defaulted on its first declaration with an exception-specification that differs
 from the implicit declaration 'constexpr std::atomic<std::pair<unsigned int, 
unsigned int> >::atomic()'

With a more recent compiler (gcc-9 with --std=c++17), I get:

In instantiation of 'struct std::atomic<std::pair<int, int> >':
  error: static assertion failed: std::atomic requires a trivially copyable type
static_assert(__is_trivially_copyable(_Tp),

demos:

I can't figure out the reason why; can anyone help me please?

YSC
  • 38,212
  • 9
  • 96
  • 149
hexiecs
  • 312
  • 1
  • 12
  • 1
    give the command line used to compile as well, this data is insufficient – bartop May 23 '19 at 12:44
  • @bartop In fact, I'm using a compile tool just like Bazel of Google, so the command line is too long and contains too much information which is not related to this problem – hexiecs May 23 '19 at 12:48

2 Answers2

11

std::atomic<T> requires T to be TriviallyCopyable.

You cannot define an std::atomic<std::pair<...>> because std::pair is not trivially copiable. For more information about that, read Why can't std::tuple be trivially copyable?.

As a workaround, you can define your own simplified trivially copiable pair:

#include <atomic>
#include <cstdint>

struct X
{
    using pair = struct { std::uint32_t first; std::uint32_t second; };
    std::atomic<pair> A;
};

demo: https://godbolt.org/z/epPvOr

YSC
  • 38,212
  • 9
  • 96
  • 149
-1

The following will provide you with the right kind of atomic pair:

#include <atomic>
#include <iostream>
#include <thread>
#include <utility>
#include <vector>

int main() {
  std::pair<std::atomic<int>, std::atomic<int>> counterPair;
  counterPair.first.store(0);
  counterPair.second.store(0);
  std::vector<std::thread> threads;
 
  for(int i = 0; i < 2; i++) {
    threads.push_back(std::thread([&counterPair, i]() {
      for(int j = 0; j < 100; j++) {
        if((i + j) % 2 == 1) {
          counterPair.first++;
        } else {
          counterPair.second++;
        }
      }
    }));
  }

  for(auto& thread : threads) {
    thread.join();
  }

  std::cout << counterPair.first.load() << ", " << counterPair.second.load() << std::endl;

  return 0;
}

demo: https://godbolt.org/z/T6f3s9xqd

  • 2
    That's only the "right kind" if you just need two separately-atomic objects, and don't need them to go together in a single atomic snapshot of both of them. – Peter Cordes Mar 07 '22 at 13:16
  • 4
    That's not an atomic pair, but a pair of atomics. It doesn't guarantee that the two elements of the pair are only modified atomically together so that they are always in a consistent state. – user17732522 Mar 07 '22 at 13:16