1

Simple question really. What is going on in the following example code that causes it not to compile?

The error occurs at the first line of main():

"Use of deleted function 'std::__atomic0::...__atomic_base(...)')"

#include <atomic>
#include <deque>
#include <vector>

using namespace std;

class Test {
public:
    deque<atomic_int> dq;
    Test(){}
};


int main(){
    vector<Test> v = { Test(), Test() };

    return 0;
}

I'm compiling it as c++0x code, in which I understand atomic types maybe can't be copied? But either way, the line vector<Test> v = { Test(), Test() }; should invoke the default move constructor/assignment operator of Test, which ought to call the move constructor/assignment operator of Test::dq, avoiding the need to copy any atomic_int.

So why won't this compile?

EDIT

My compiler will allow me to add non-moveable objects to a container, and then move the container. See below:

class Test {
public:
    deque<atomic_int> dq;
    Test(){
        dq.resize(10);
    }
};


int main(){
    Test t1;
    Test t2(std::move(t1));

    return 0;
}

A move is performed on t1, which invokes a move of t1's members, one of which is a deque. So a move of each member in deque occurs, none of which are of type atomic_int

AndyMcoy
  • 179
  • 1
  • 3
  • 13
  • deque of atomic_int's makes no sense – paulm Mar 19 '15 at 18:06
  • Care to elaborate on why a `deque` makes no sense? – AndyMcoy Mar 19 '15 at 18:16
  • @AndyMcoy I'm guessing the standard (containers) writers didn't consider the case where a type is assignable but not copy constructible? Or they specifically excluded those kinds of types. – jschultz410 Mar 19 '15 at 18:23
  • @AndyMcoy Another thing, move constructors weren't in the standard until C++11, so maybe your compiler is doing something non-standard? – jschultz410 Mar 19 '15 at 18:28
  • because deque isn't atomic so it makes no sense to put an atomic type in there, you'd need to lock on the deque itself – paulm Mar 19 '15 at 22:40
  • @paulm Yes, normally that would be true if the deque itself is going to have elements added/removed from seperate threads, but in my case the deque size will remain fixed after initialization, removing any need to lock it before accessing its elements. – AndyMcoy Mar 20 '15 at 00:36
  • Then why are you using deque? use std::array – paulm Mar 20 '15 at 06:43
  • I use a `deque` because I don't know the what the size will be until the containing class' constructor is called. I don't know of any way to declare a `std:array` without specifying a size? ... – AndyMcoy Mar 20 '15 at 10:27

1 Answers1

3

Since C++11, having a container of only default constructible objects is perfectly legal, provided that you do not use any operation that requires the object to be copyable or movable.

However, std::initializer_list allows only const access to its elements, meaning that you can't move from them. Hence

vector<Test> v = { Test(), Test() };

would attempt to copy a Test, which is not valid because it would attempt to copy the deque.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Would this mean that `vector` is not allowed even after C++11 if you do push_back's, etc.? – jschultz410 Mar 19 '15 at 18:45
  • Objects of type `` can't be copied, so `vector` is illegal regardless of how you use the vector, because the vector class will require existing elements to be copied into new memory locations as the vector grows and travels through memory. – AndyMcoy Mar 19 '15 at 18:58
  • @AndyMcoy Only if I cause the vector to reallocate, right? Or, at least, it could be implemented that way, I think. – jschultz410 Mar 19 '15 at 19:00
  • Yes that's right, however, I believe the vector class just rules out non-copyable types outright. E.g mutexes, references, atomics, none of which can be used to create a vector – AndyMcoy Mar 19 '15 at 19:03
  • 1
    Since C++11: "The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type is a complete type and meets the requirements of Erasable, but many member functions impose stricter requirements." So, I guess it actually depends on how exactly std::vector is implemented in your C++ std library. – jschultz410 Mar 19 '15 at 19:04
  • @jschultz410 The standard spells out the requirements on the value type for each operation. – T.C. Mar 19 '15 at 19:09
  • 1
    @AndyMcoy You can do a `vector f(10);`, and call `pop_back()` or `clear()` on it. That's about it. – T.C. Mar 19 '15 at 19:10