Non-Copyable Elements in Vector

Non-copyable elements in vector

Yes you can have std::vector<NotCopyable> if NotCopyable is movable:

struct NotCopyable
{
NotCopyable() = default;
NotCopyable(const NotCopyable&) = delete;
NotCopyable& operator = (const NotCopyable&) = delete;

NotCopyable(NotCopyable&&) = default;
NotCopyable& operator = (NotCopyable&&) = default;
};

int main()
{
std::vector<NotCopyable> v;
NotCopyable nc;

v.push_back(NotCopyable{});
v.emplace_back();
v.push_back(std::move(nc));
}

Live example.

Non-copyable elements in a std::vector of std::lists

std::list is not noexcept-movable, but is copyable. Therefore, std::vector prefers list's the copy-constructor over its move-constructor, regardless.

To work around the issue, you have to wrap your std::list in a class that is not copyable.

Using vectors with classes that contain non-copyable data types

You might try using a vector of pointers to your objects. It is better to use some kind of smart pointer, rather than a vector of raw pointers.

i.e.
std::vector< std::shared_ptr< MyClass >> MyVector;

now you have a container of pointers to MyClass objects.

How to contruct non-copyable, non-moveable class in a nested vector?

You can use std::vector<std::mutex> directly, because the type requirements when using standard library containers are restricted to only those required by the functions called on them.

Once you have constructed a std::vector<std::mutex> with some elements, only the operations that might add new elements or erase old ones require the value type to be movable. Accessing the vector elements and moving the vector itself are not problems.

Constructing the vector with vec(num) works, because it only default constructs a known number of elements. It can constructs the new elements in-place in the storage.

The constructor used by vec(numB, numA) actually takes as arguments the number of elements and a const lvalue reference to an object of the value type. It does not take constructor arguments to construct the new elements from in-place. Instead when you pass it numA, numA is implicitly converted to a A (via the non-explicit constructor) and a reference to that A is passed to the constructor.
The constructor is then specified to copy-construct the vector elements from the passed object.
But because std::vector<std::mutex> vec; is not copyable, A isn't either and so it fails.

There is however another constructor for std::vector that can construct objects without copy/move constructor: The constructor taking an iterator range. However, to use it we first need to construct an iterator range with the constructor arguments to pass to the vector elements' constructor:

auto args = std::vector(numB, numA);
vec = {args.begin(), args.end()};

Alternatively, with explicit types:

std::vector<int> args(numB, numA);
vec = std::vector<A>(args.begin(), args.end());

If you want to do this in the member-initilizer-list you can delegate this to a member function or lambda and return instead of vec =.

The iterator range constructor construct the vector elements in-place by converting *it and if the iterators are forward iterators (as is the case above), then the constructor does not require any move operations.

Note that vec.assign(args.begin(), args.end()) does not work, since assign is allowed to use assignment instead of construction.

In place construction of a pair of nonmovable, non copyable in a std::vector

std::vector is subject to reallocation once the size reach the capacity.
when reallocating the elements into a new memory segment std::vector has to copy/move the values from the old segment and this is made by calling copy/move constructors.

if you don't need that the elements are sequential in memory you can use std::deque instead, since std::deque doesn't reallocate the elements internally.

you can't store non copyable and non moveable objects into std::vectors.


EDIT suggested by @François Andrieux

In case you still need for any reason an std::vector you may think to use a vector made using std::unique_ptr<X> as value type using std::vector<std::unique_ptr<X>>.

With this solution you still don't get a sequential order in memory of your elements, and they are keep in memory till they are still in the vector, so except in case you are forced by any reason to use std::vectors, i think the best match is still the std::deque.

How to create a vector with non-copyable and non-assignable objects?

std::unique_ptr is not copyable, so you need to move it into the container:

for (int i = 0; i < 10; i++) {
auto a = MakeUnique<A>();
a->SetValue(20);
objects.emplace_back(std::move(a));
}

C++ container with non-copyable non-movable element type

Here's a simple, yet incomplete solution under the assumption that each element is constructed with the same arguments. It uses placement new to construct the elements in-place (see also this SO question):

#include <cstdlib>
#include <utility>
#include <new>

// sample structure, non-copyable, non-moveable, non-default-constructible
struct Foo
{
Foo() = delete;
Foo(const Foo&) = delete;
Foo& operator = (const Foo&) = delete;
Foo(Foo&&) = delete;
Foo& operator = (Foo&&) = delete;

Foo(int a, char b, double c) : m_a(a), m_b(b), m_c(c) { }

int m_a;
char m_b;
double m_c;
};

template <typename T>
struct MyArray
{
// Array ctor constructs all elements in-place using the
// provided parameters
template <typename... Args>
MyArray(std::size_t sz, Args&&... args)
: m_sz(sz),
m_data(static_cast<T*>(malloc(sz * sizeof(T))))
{
for (std::size_t i=0; i<m_sz; ++i)
{
new (&m_data[i]) T(std::forward<Args>(args)...);
}
}

~MyArray()
{
for (std::size_t i=0; i<m_sz; ++i)
{
m_data[i].~T();
}
free(m_data);
}

std::size_t m_sz;
T *m_data;
};

int main()
{
Foo foo(1, '2', 3.0);
std::size_t s = 5;
MyArray<Foo> foo_arr(s, 1, '2', 3.0);
}

Note that a few things are missing:

  • This basic implementation will leak memory if an exception is thrown inside MyArray's constructor.
  • You will probably want an iterator implementation, begin()/end() operators etc., for more convenience and to get the same behaviour as provided by the standard containers.
  • For illustration's sake I also didn't bother with proper encapsulation. You should probably make m_sz and m_data private members.

std::for_each over a vector of non-copyable objects

Your lambda capture block attempts to capture the whole vector by value. This is unnecessary, since the access to the elements is granted through the reference argument.

Try this:

std::vector<std::thread> threads;

std::for_each(threads.begin(), threads.end(), [](std::thread & t){t.join();});

Using vectors of non-copyable objects

Store a pointer instead. std::unique_ptr<sf::RenderWindow> should be suitable. Initialize it with new or std::make_unique<sf::RenderWindow> (C++14).

Note that your main loop will need to be radically different as you would need to poll several windows. pollEvent is unsuitable as it blocks: if you call it on wnd1, you won’t be able to process events arriving to other windows until some event arrives to wnd1 as well.



Related Topics



Leave a reply



Submit