Reputation: 228
I understand that atomic has a copy constructor that's deleted, but what can I do to make this code work? How can I possibly define a copy constructor inside vector for atomic?
#include <atomic>
#include <vector>
int main() {
std::vector<std::atomic<int>> examp;
examp.resize(64);
}
Upvotes: 2
Views: 5583
Reputation: 41
I have tried the following code in VS 2022 and it works:
#include <atomic>
#include <vector>
int main() {
std::vector<std::atomic<int>> examp (64);
}
However, as the std::atomic
type is not copyable or movable, some member functions (such as resize()
) could not be implemented. For a vector of atomic variables, many member functions impose stricter requirements.
See https://en.cppreference.com/w/cpp/container/vector
Upvotes: 1
Reputation: 364278
If you just need a runtime-variable size but don't need to resize the object after that, you can use std::vector< atomic<int> > elems(size)
as @Quanbing Luo points out.
Member functions like .pop_back
and .push_back
won't compile, even if you've arranged things so it definitely won't exceed its .capacity()
and have to grow. But other than that it should work fine.
In C++20, you can use std::atomic_ref
to do atomic operations on a plain int
object, so you can use std::vector<int>
and
static_assert(std::atomic_ref<int>::required_alignment == alignof(int), "vector elements need to be sufficiently aligned for atomic_ref");
std::vector<int> examp(size); // and potentially do whatever non-atomic init before sharing
examp.resize(size2);
std::atomic_ref<int> examp3(examp[3]);
examp3.store(1, std::memory_order_release);
Perhaps use a typedef
or something instead of bare int
everywhere. And maybe even helper functions for some atomic ops so you don't have to manually construct an atomic_ref
object in a separate statement. (Constructing one is free; it'll optimize away as long as you don't keep the atomic_ref
object around. It only exists as an API to wrap stuff like GNU C __atomic_load_n(int *, int memorder)
, which you could use manually if you don't want to use C++20 features.)
Nothing can make it thread-safe to grow or shrink a vector while other threads are accessing elements of it, so there's no upside to the extra level of indirection in another answer's proposed std::vector<std::unique_ptr<std::atomic<int>>> examp;
. The std::vector<unique_ptr>
object itself (typically 3 pointers, .data(), .end() and end-of-allocation) is not atomic, and anything that reallocates the array while another thread is accessing it is a problem.
Writing inefficient code just to keep the compiler happy is kind of at odds with the purpose of using lock-free atomics instead of locking (performance at the expense of simplicity).
Upvotes: 0
Reputation: 1026
You can't have a vector of std::atomic<int>
because it is not copyable or movable, but you can have a vector of unique_ptrs to atomic<int>
. If you really need a run-time variable-size vector of atomics, this may be a viable alternative. Here is an example:
#include <iostream>
#include <atomic>
#include <vector>
#include <memory>
using namespace std;
int main() {
std::vector<std::unique_ptr<std::atomic<int>>> examp;
examp.resize(64); // 64 default unique_ptrs; they point to nothing
// init the vector with unique_ptrs that actually point to atomics
for (auto& p : examp) {
p = std::make_unique<std::atomic<int>>(0); // init atomic ints to 0
}
// use it
*examp[3] = 5;
for (auto& p : examp) {
cout << *p << ' ';
}
cout << '\n';
}
Upvotes: 6
Reputation: 183
std::atomic
is not copyable or movable. As you noted, the copy constructor is deleted but no move constructor is generated. See http://en.cppreference.com/w/cpp/language/move_constructor:
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
- there are no user-declared copy constructors;
User-declared means "not added by the compiler" (i.e by default
). Even though it is a library class, the constructor is user-declared.
The resize
function for a vector requires that the type either be move-insertable or copy-insertable depending on the overload. See http://en.cppreference.com/w/cpp/container/vector/resize:
If the current size is less than count,
1) additional default-inserted elements are appended
2) additional copies of value are appended
What you're doing simply won't work.
Upvotes: 3