22

I have a type that I have deleted the copy constructor from, and I would like to have a vector of this type, so I need to create all the elements via emplace_back. But, emplace_back seems to require a copy constructor, as the compiler gives a warning about not being able to instantiate emplace_back because the copy constructor has been deleted. Why does it need a copy constructor? I thought the whole point of emplace_back was to build the vector without copying anything. Can I even have a vector of objects that don't have a copy constructor?

class MyType {
public:
    MyType(std::array<double, 6> a) {}
    MyType(const MyType& that) = delete;
};

int main() {
    std::vector<MyType> v;
    std::array<double, 6> a = {1,2,3,4,5,6};
    v.emplace_back(a);
}

Compiler is clang/llvm.

Drew
  • 12,578
  • 11
  • 58
  • 98
  • http://cpp.sh/9v5w here does not work – Humam Helfawi Dec 31 '15 at 09:06
  • here also http://goo.gl/eLUPjR – Humam Helfawi Dec 31 '15 at 09:09
  • `emplace_back` don't need the copy constructor but when the capacity of vector is not enough to store new element, vector need to reallocate its element, this requires copy/move constructor. By deleting the copy constructor, you also prevent it generating the default move constructor. In this case, declare a move constructor may work – Danh Dec 31 '15 at 09:26

3 Answers3

34

When the vector's internal storage grows, it will need to move the elements from the old storage to the new. By deleting the copy constructor, you also prevent it generating the default move constructor.

BoBTFish
  • 19,167
  • 3
  • 49
  • 76
12

To be able to call emplace_back, your type should either be EmplaceConstructible or MoveInsertible . You need to give a move constructor to your class if you have deleted the copy constructor. (Check this for requirements of emplace_back)

 MyType(MyType &&a) {/*code*/} //move constructor
bashrc
  • 4,725
  • 1
  • 22
  • 49
2

If you try to run this code:

// Example program
#include <iostream>
#include <string>
#include <array>
#include <vector>
class MyType {
public:
    MyType(std::array<double, 6> a) {
        std::cout<< "constructed from array\n";
        }
    MyType(const MyType& that){
          std::cout<< "copy\n";
    }

    MyType(MyType&& that){
          std::cout<< "move\n";
    }
};

int main() {
    std::vector<MyType> v;
    std::array<double, 6> a = {1,2,3,4,5,6};
    v.emplace_back(a);
}

You will get the following result:

constructed from array

Live Demo

It is clear that just the constructor from std::Array is called. So, no need for copy constructor. But in the same time if you deleted the copy constructor, the compiler will raise an error (at least on two compilers I tried first second ). I think that some compilers will check for the existence of copy constructor when using emplace_back even if it is not necessary in this practical case while others won't. I do not know what is standard here (which compiler is right or wrong).

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • 1
    The problem is that `emplace_back` can cause the vector to grow, which needs the objects inside to be movable. And since `MyType` has a user-declared copy constructor, no move constructor is generated and the class is neither copyable nor movable. – Angew is no longer proud of SO Dec 31 '15 at 09:20
  • 1
    but at least in this case no copy or move was called.. so what is the difference ? – Humam Helfawi Dec 31 '15 at 09:23
  • @HumamHelfawi It could be required eventually. Which means there is some code which will be there to handle that case. And for that either the move constructor and copy constructor must be present at compile time. – bashrc Dec 31 '15 at 09:33
  • The difference is that the code for `emplace_back` contains a call to the move ctor. Control would not have passed through it at runtime, but it must be compilable anyway. – Angew is no longer proud of SO Dec 31 '15 at 09:34