2

Can I create vector that contains elements that are noncopyable and don't have a default constructor in C++11?

example:

#include <iostream>
#include <string>
#include <vector>

struct value {
    value() = delete;
    ~value() = default;

    value(value const&) = delete;
    value& operator =(value const&) = delete;

    explicit value(int i) : i_(i) {}
private:
    int i_;
};

int main() {
    std::vector<value> v;
    v.reserve(10);
    for (unsigned i = 0; i < 10; ++i)
        v.emplace_back(7);
}

and here I want to create 10 values and each to value ctor pass the integer 7 ...

std::vector< value > v(in-place, 10, 7)

Why wasn't the C++11 placement construction form added to this std::vector constructor

Errors pasted from coliru:

+ g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
In file included from /usr/include/c++/4.8/vector:62:0,
                 from main.cpp:3:
/usr/include/c++/4.8/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = value; _Args = {value}]’:
/usr/include/c++/4.8/bits/stl_uninitialized.h:75:53:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<value*> _ForwardIterator = value*; bool _TrivialValueTypes = false]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:117:41:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<value*> _ForwardIterator = value*]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:258:63:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<value*> _ForwardIterator = value*; _Tp = value]’
/usr/include/c++/4.8/bits/stl_vector.h:1142:29:   required from ‘std::vector<_Tp, _Alloc>::pointer std::vector<_Tp, _Alloc>::_M_allocate_and_copy(std::vector<_Tp, _Alloc>::size_type, _ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::move_iterator<value*> _Tp = value; _Alloc = std::allocator<value> std::vector<_Tp, _Alloc>::pointer = value*; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
/usr/include/c++/4.8/bits/vector.tcc:75:70:   required from ‘void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = value; _Alloc = std::allocator<value> std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
main.cpp:24:17:   required from here
/usr/include/c++/4.8/bits/stl_construct.h:75:7: error: use of deleted function ‘value::value(const value&)’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^
main.cpp:11:5: error: declared here
     value(value const&) = delete;
     ^
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
Khurshid Normuradov
  • 1,564
  • 1
  • 13
  • 20
  • All the answers so far assume that the items are (or can sensibly be made to be) moveable. I'd like to see an answer without this assumption. – Don Hatch Mar 27 '15 at 22:22
  • That sounds like a separate question - why don't you ask it? – Useless Mar 28 '15 at 17:06

4 Answers4

3

The reason you get errors is that using emplace_back on a std::vector requires the element type to be at least MoveConstructible. This is needed if the vector needs to grow and reallocate its elements.

Add a move constructor to you struct and you will be able to use it in your code (the default implementation will suffice for your code).

value(value&&) = default;

The compiler will not implicitly generate a default move constructor for your struct as you have declared your own copy constructor, value(value const&) = delete (=delete and =default count as user-declared), as well as a copy assignment operator and a destructor.

For more info about the rules of implicit move constructor generation, look here: Why no default move-assignment/move-constructor?


Using std::vector's constructor of the form std::vector(size_t count, const T& value) copies the values into the vector, and requires the element type to be CopyConstructible.

Community
  • 1
  • 1
Felix Glas
  • 15,065
  • 7
  • 53
  • 82
  • 1
    Declaring a constructor does not in general suppress generation of the default move constructor: declaring a *copy* constructor does suppress generation of a default move constructor (§12.8p9). – Casey Jul 30 '13 at 16:26
2

Yes, the resizing etc. can all be handled with move instead of copy constructor/assignment, if you define it (whatever cppreference says).

#include <vector>
#include <iostream>

struct value {
    int i_;

    explicit value(int i) : i_(i)   {std::cout<<"value::value("  <<i_<<")\n"; }
    value(value &&src) : i_(src.i_) {std::cout<<"value::value(&&"<<i_<<")\n"; }
    value(value const&) = delete;
    value& operator=(value const&) = delete;
    value& operator=(value &&src) {
        i_ = src.i_;
        std::cout << "value::=(&&" << i_ << ")\n";
        return *this;
    }
    value() = delete;
};

int main() {
    std::vector<value> v;
    v.reserve(1);
    v.emplace_back(1);
    v.emplace_back(2);
    v.emplace_back(3);
}

works fine:

value::value(1)   <-- emplace_back(1)
value::value(2) 
value::value(&&1) <-- emplace_back(2) inc. resize & move 1
value::value(3)
value::value(&&1)
value::value(&&2) <-- emplace_back(3) inc. resize & move 1,2

Motivation for the requirement

Before move support, if a vector's elements were non-copyable, it would be unable to resize itself (or erase items, or fulfill quite a lot of its interface).

Useless
  • 64,155
  • 6
  • 88
  • 132
  • std::array is only static size, if I need dynamic size I can't use it. – Khurshid Normuradov Jul 30 '13 at 12:37
  • I missed that you hadn't defined move constructor & assignment, given you're asking about C++11. That's all you need. – Useless Jul 30 '13 at 13:12
  • for emplace_back needn't move ctor and assigment, if enough memory already exists, so why I should defined them? – Khurshid Normuradov Jul 31 '13 at 05:16
  • if the vector has to resize dynamically, it needs to move your already-emplaced items to a new allocated block. – Useless Jul 31 '13 at 07:20
  • Note that even if the move code is never _called_, it still needs to be _instantiated_. Even if the compiler is smart enough to analyze your run-time constraint at compile time, and optimize that code out, it still needs to be legally instantiatable. – Useless Aug 02 '13 at 12:17
  • This all assumes the items are (or can sensibly be made to be) "movable". What if they aren't? Seems to me a valid use case is: create an empty vector, reserve it to size n, emplace_back n items. This should not invoke any copying or moving, right? But it seems I can't do this unless I define a move constructor that will never be called (so I can make it throw a fatal runtime error, I guess). – Don Hatch Mar 27 '15 at 22:20
  • You're entirely free to write a runtime-sized-but-non-growable container if you want - the standard library just doesn't provide one. `std::vector` has _lots_ of methods that can't be instantiated at all if elements are neither copyable nor moveable. At the very least it would need to provide a separate _non-growing_ overload of `emplace_back` that somehow returned an error, to avoid trying to instantiate the resizing code. – Useless Mar 28 '15 at 17:05
1

Yes, you can put objects that aren't copyable or default-constructible in a vector in C++11, if they are movable:

#include <iostream>
#include <string>
#include <vector>

struct value {
    value() = delete;
    ~value() = default;

    value(value const&) = delete;
    value& operator =(value const&) = delete;

    // Move construction and assignment
    value(value&&) = default;
    value& operator =(value&&) = default;

    explicit value(int i) : i_(i) {}
private:
    int i_;
};

int main() {
    std::vector<value> v;
    v.reserve(10);
    for (unsigned i = 0; i < 10; ++i)
        v.emplace_back(7);
}

But you can't use the constructor that fills such a vector with copies of a given value - since the values are not copyable. Similarly, you can't grow a vector of such objects with resize.

Casey
  • 41,449
  • 7
  • 95
  • 125
0

Write a move constructor and then use emplace_back:

struct value
{
    ...

    value(value && obj) : i_(obj.i_)
    {
    }

    or 

    value(value && obj) = default;

    ...
};

std::vector<value> v;

for (int i=0; i<10; i++)
   v.emplace_back(7);
masoud
  • 55,379
  • 16
  • 141
  • 208