I'm performing some tests about move semantics, and my class behavior seems weird for me.
Given the mock class VecOfInt:
class VecOfInt {
public:
VecOfInt(size_t num) : m_size(num), m_data(new int[m_size]) {}
~VecOfInt() { delete[] m_data; }
VecOfInt(VecOfInt const& other) : m_size(other.m_size), m_data(new int[m_size]) {
std::cout << "copy..." <<std::endl;
std::copy(other.m_data, other.m_data + m_size, m_data);
}
VecOfInt(VecOfInt&& other) : m_size(other.m_size) {
std::cout << "move..." << std::endl;
m_data = other.m_data;
other.m_data = nullptr;
}
VecOfInt& operator=(VecOfInt const& other) {
std::cout << "copy assignment..." << std::endl;
m_size = other.m_size;
delete m_data;
m_data = nullptr;
m_data = new int[m_size];
m_data = other.m_data;
return *this;
}
VecOfInt& operator=(VecOfInt&& other) {
std::cout << "move assignment..." << std::endl;
m_size = other.m_size;
m_data = other.m_data;
other.m_data = nullptr;
return *this;
}
private:
size_t m_size;
int* m_data;
};
OK CASE
When I insert a single value in-place:
int main() {
std::vector<VecOfInt> v;
v.push_back(10);
return 0;
}
Then it gives me the following output (what I think is fine):
move...
WEIRD CASE
When I insert three different values in-place:
int main() {
std::vector<VecOfInt> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
return 0;
}
Then the output calls the copy constructor 3 times:
move...
move...
copy...
move...
copy...
copy...
What I'm missing here?
Move construction and move assignment aren't used by std::vector when reallocating unless they are noexcept or if there is no copying alternatives. Here is your example with noexcept added :
class VecOfInt {
public:
VecOfInt(size_t num) : m_size(num), m_data(new int[m_size]) {}
~VecOfInt() { delete[] m_data; }
VecOfInt(VecOfInt const& other) : m_size(other.m_size), m_data(new int[m_size]) {
std::cout << "copy..." <<std::endl;
std::copy(other.m_data, other.m_data + m_size, m_data);
}
VecOfInt(VecOfInt&& other) noexcept : m_size(other.m_size) {
std::cout << "move..." << std::endl;
m_data = other.m_data;
other.m_data = nullptr;
}
VecOfInt& operator=(VecOfInt const& other) {
std::cout << "copy assignment..." << std::endl;
m_size = other.m_size;
delete m_data;
m_data = nullptr;
m_data = new int[m_size];
m_data = other.m_data;
return *this;
}
VecOfInt& operator=(VecOfInt&& other) noexcept {
std::cout << "move assignment..." << std::endl;
m_size = other.m_size;
m_data = other.m_data;
other.m_data = nullptr;
return *this;
}
private:
size_t m_size;
int* m_data;
};
A live live example outputs :
move...
move...
move...
move...
move...
move...
This is done to keep exception safety. When resizing a std::vector fails, it will try to leave the vector as it was before the attempt. But if a move operation throws half way through reallocation, there is no safe way to undo the moves that have already been made successfully. They very well may also throw. The safest solution is to copy if moving might throw.
std::vector allocates a block of contiguous memory for its elements. When the allocated memory is too short for storing new elements, a new block is allocated and all current elements are copied from the old block to the new block.
You can use std::vector::reserve() to pre-size the capacity of the std::vector memory before adding new elements.
Try the following:
int main() {
std::vector<VecOfInt> v;
v.reserve(3);
v.push_back(10);
v.push_back(20);
v.push_back(30);
return 0;
}
and you will get:
move...
move...
move...
But to get move constructor being called even when reallocating you should make it noexcept like:
VecOfInt(VecOfInt&& other) noexcept {...}
tl;dr: std::vector will copy instead of moving if your move constructor isn't noexcept.
1. It's not about the members and the dynamic allocations
The issue is not with what you do with the fields of foo. So your source could be just:
class foo {
public:
foo(size_t num) {}
~foo() = default
foo(foo const& other) {
std::cout << "copy..." <<std::endl;
}
foo(foo&& other) {
std::cout << "move..." << std::endl;
}
foo& operator=(foo const& other) {
std::cout << "copy assignment..." << std::endl;
return *this;
}
foo& operator=(foo&& other) {
std::cout << "move assignment..." << std::endl;
return *this;
}
};
and you still get the same behavior: try it.
2. The move's you do see are a distraction
Now, push_back() will first construct an element - foo in this case; then make sure there's space for it in the vector; then std::move() it into its place. So 3 of your moves are of that kind. Let's try using emplace_back() instead, which constructs the vector element in its place:
#include <vector>
#include <iostream>
struct foo { // same as above */ };
int main() {
std::vector<foo> v;
v.emplace_back(10);
v.emplace_back(20);
v.emplace_back(30);
return 0;
}
This gives us:
copy
copy
copy
try it. So the moves were just a distraction really.
3. The copies are due to the vector resizing itself
Your std::vector gradually grows as you insert elements - necessitating either moves or copy constructions. See #NutCracker's post for details.
4. The real issue is exceptions
See this question:
How to enforce move semantics when a vector grows?
std::vector doesn't know it can safely move elements when resizing - where "safely" means "without exceptions", so it falls back to copying.
5. "But my copy ctor can throw an exception too!"
I guess the rationale is that if you get an exception while copying the smaller buffer - you still haven't touched it, so at least your original, non-resized vector is valid and can be used. If you started moving elements and got an exception - then you have no valid copy of the element anyway, not to speak of a valid smaller vector.
Your move constructor does not have the specifier noexcept.
Declare it like
VecOfInt(VecOfInt&& other) noexcept : m_size(other.m_size) {
std::cout << "move..." << std::endl;
m_data = other.m_data;
other.m_data = nullptr;
}
Otherwise the class template std::vector will call the copy constructor.
Add noexcept decorators to your move constructor and move assignment operator:
VecOfInt(VecOfInt&& other) noexcept : m_size(other.m_size) {
std::cout << "move..." << std::endl;
m_data = other.m_data;
other.m_data = nullptr;
}
VecOfInt& operator=(VecOfInt&& other) noexcept {
std::cout << "move assignment..." << std::endl;
m_size = other.m_size;
m_data = other.m_data;
other.m_data = nullptr;
return *this;
}
Some functions (for instance, std::move_if_noexcept, used by std::vector) will decide to copy your object if its move operations are not decorated with noexcept., i.e. there is no guarantee they will not throw. That's why you should aim to make your move operations (move constructor, move assignment operator) noexcept. This can significantly improve the performance of your program.
According to Scott Meyer's in Effective Modern C++:
std::vector takes advantage of this "move if you can, but copy if
you must" strategy , and it's not the only function in the Standard
Library that does. Other functions sporting strong exception safety
guarantee in C++98 (e.g. std::vector::reserve, std::deque::insert,
etc) behave the same way. All these functions replace calls to copy
operations in c++98 with calls to move operations in C++11 only if the
move operations are known not to emit exceptions. But how can a
function know if a move operation won't produce an exception? The
answer is obvious: it checks to see if the operation is declared
noexcept.
Related
I wanted to know what exactly happens internally when we call std::move on an object.
For example:
class Holder {
int* m_ptr;
int m_size;
public:
Holder(int size) : m_size(size),
m_ptr(size ? new int[size] : 0) {
cout << "Constructor\n";
}
~Holder() {
cout << "Destructor\n";
delete[] m_ptr;
}
Holder(const Holder& other) {
cout << "Copy Constructor\n";
m_ptr = new int[other.m_size];
std::copy(other.m_ptr, other.m_ptr + other.m_size, m_ptr);
m_size = other.m_size;
}
void swap(Holder& other) noexcept {
std::swap(this->m_ptr, other.m_ptr);
std::swap(this->m_size, other.m_size);
}
Holder& operator=(const Holder& other) {
cout << "Copy Assignment\n";
Holder(other).swap(*this);
}
Holder(Holder&& other) : m_ptr(other.m_ptr), m_size(other.m_size) {
cout << "Move Constructor\n";
other.m_ptr = nullptr;
other.m_size = 0;
}
Holder& operator=(Holder&& that) noexcept {
cout << "Move Assignment\n";
Holder(std::move(that)).swap(*this);
return *this;
}
};
Holder createHolder(int size) {
return Holder(size);
}
int main(void) {
Holder h = createHolder(1000);
return 0;
}
Gives the output as when compiled with '-fno-elide-constructors':
Constructor
Move Constructor
Destructor
Move Constructor
Destructor
Destructor
Since we are returning from a function createHolder and the return object is on the stack, how does the main function still receives it? Not getting exactly what happens internally in memory.
Thanks.
std::move is a noop function that converts any kind of reference into an rvalue reference. So it doesn't actually "do" anything, it just changes how it's argument is used.
template <class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept;
Returns: static_cast<remove_reference_t<T>&&>(t).
std::move make your operator =, constructor is call correctly with what you defined in the Holder class
Holder(Holder&& other) : m_ptr(other.m_ptr), m_size(other.m_size) {
cout << "Move Constructor\n";
other.m_ptr = nullptr;
other.m_size = 0;
}
Holder& operator=(Holder&& that) noexcept {
cout << "Move Assignment\n";
Holder(std::move(that)).swap(*this);
return *this;
}
Since we are returning from a function createHolder and the return object is on the stack, how does the main function still receives it?
because your move constructor
you already copy pointer m_ptr(other.m_ptr) m_size(other.m_size) from
Holder(size) -> move to Holder object in return register on stack -> move to Holder h in main function
Constructor -> of Holder(size);
Move Constructor -> Holder(size) move to Holder object in return register on stack
Destructor -> destructor of Holder(size)
Move Constructor -> from Holder object on stack -> Holder h in main
Destructor -> destructor of Holder object in return register
Destructor -> destructor of Holder h in main
Implementing a reset() method, which uses some of the members from this to construct a new object and then move-assign that object to *this.
Questions:
Does this cause any problems? UB or otherwise? (seems fine to me?)
Is there a more idiomatic way?
Destructor and move-assignment operator only implemented to prove what is happening/ The real class has neither. "Rule of 5" not followed, as not needed here.
#include <algorithm>
#include <cstddef>
#include <iostream>
#include <numeric>
#include <ostream>
#include <random>
#include <vector>
struct Thing { // NOLINT Rule of 5 not followed, because debug only
public:
std::size_t size;
std::vector<int> v;
explicit Thing(std::size_t size_) : size(size_), v(size_) {
std::cerr << "Thing constructor\n";
std::iota(v.begin(), v.end(), 1);
}
~Thing() { std::cerr << "Thing destructor\n"; } // purely for debugging
Thing& operator=(Thing&& other) noexcept { // purely for debugging
std::cerr << "Thing Move Assignment operator\n";
size = other.size;
v = std::move(other.v);
return *this;
}
void shuffle() {
std::shuffle(v.begin(), v.end(), std::mt19937{1}); // NOLINT fixed seed
}
void reset() {
// move assignment operator from a newly constructed object. Constructor uses SOME of the
// member variables of *this
*this = Thing(size);
}
friend std::ostream& operator<<(std::ostream& os, const Thing& t) {
os << "[";
const char* delim = "";
for (const auto& e: t.v) {
os << delim << e;
delim = ",";
}
return os << "]";
}
};
int main() {
std::cerr << "Before inital construction\n";
auto t = Thing(10);
std::cout << t << "\n";
t.shuffle(); // somehow modify the object
std::cerr << "Before reset\n";
std::cout << t << "\n";
t.reset(); // reset using
std::cerr << "after reset\n";
std::cout << t << "\n";
}
Output:
Before inital construction
Thing constructor
[1,2,3,4,5,6,7,8,9,10]
Before reset
[10,1,3,6,8,5,7,4,2,9]
Thing constructor
Thing Move Assignment operator
Thing destructor
after reset
[1,2,3,4,5,6,7,8,9,10]
Thing destructor
Does this cause any problems? UB or otherwise? (seems fine to me?)
As long as the move assignment and the constructor are implemented in such a way that it does what you want, then I don't see a problem.
Is there a more idiomatic way?
I would invert the direction of re-use.
explicit Thing(std::size_t size_) : size(size_), v(size_) {
reset();
}
void reset() {
v.resize(size_);
std::iota(v.begin(), v.end(), 1);
}
If size is supposed to always match the size of the vector, then I would recommend removing the member since the size is already stored inside the vector. This has a caveat that in such case reset won't be able to restore a moved-from thing to its earlier size. But that's typically reasonable limitation.
I am writing a template function which accepts a custom class (that can be any class or primitive type) as a template argument, then reads some data (of that type) from an input stream, and then stores it an unordered map similar to this one:
std::unordered_map<CustomClass, std::vector<CustomClass>> map;
I have implemented a custom class to test the behavior. I have overloaded the std::hash so that this class can be stored in an unordered map as a key and overloaded all operators and constructors such that whenever they are called, I get a message in the console (example, when a copy constructor is called, I get a message "copy constructor [..data...]")
EDIT: As requested in the comments, here is the custom class definition and implementation (please note: the class here is only a placeholder so we can discuss the general idea behind this question. I am well aware that it is dumb and should not be implemented like this. The code for operators >> and << is not here, to avoid clutter)
class CustomClass {
public:
CustomClass(int a=0) {
std::cout << "default constructor" << std::endl;
m_data = a;
}
CustomClass(const CustomClass& other) {
std::cout << "copy constructor " ;//<< std::endl;
m_data = other.m_data;
std::cout << "[" << m_data << "]" << std::endl;
}
CustomClass(CustomClass&& other) {
std::cout << "move cosntructor" << std::endl;
m_data = other.m_data;
}
CustomClass& operator=(const CustomClass& other) {
std::cout << "copy assignment operator" << std::endl;
if(this != &other){
m_data = other.m_data;
}
return *this;
}
CustomClass& operator=(CustomClass&& other) {
std::cout << "move assignment operator" << std::endl;
if(this != &other){
m_data = other.m_data;
}
return *this;
}
~CustomClass() {
std::cout << "destructor" << std::endl;
}
int m_data;
};
Now my question is this: Is it possible to read data from the input stream and construct it inplace where it is needed without a copy constructor call?
Example of some code:
CustomClass x1; // default constructor call
CustomClass x2; // default constructor call
std::cout << "----" << std::endl;
std::cin >> x1 >> x2; // my input
std::cout << "----" << std::endl;
map[x1].emplace_back(x2); // 2 copy constructor calls
std::cout << "----" << std::endl;
std::cout << map[x1][0] << std::endl; // operator== call
std::cout << "----" << std::endl;
And here is an example output from that code:
default constructor
default constructor
----
[1]
[2]
----
copy constructor [1]
copy constructor [2]
----
operator ==
[2]
----
destructor
destructor
destructor
destructor
I would like to have it so that every object of this class is constructed only once.
Is it possible to avoid these copy constructors? If not both, then at least the one that is called during the emplace_back() call? Is it possible to construct the object in the vector exactly where it needs to be in memory but that this sort of call works for every type?
If I need to further elaborate on my question please tell me in the comments, I will be happy to do so
So you have an std::vector and you want to put an element there avoiding unnecessary copying. Assuming that move constructor for your class is cheap, the first option is to define a non-trivial constructor, read the parameters from stream and then emplace_back a newly created object:
using CustomClass = std::vector<int>;
std::vector<CustomClass> v;
size_t size;
int value;
std::cin >> size >> value;
v.emplace_back(size, value);
Here I defined the CustomClass as a vector of integers, and it has a constructor that takes 2 parameters: size and the value. For sure it is cheaper to read there two integers and create the instance of CustomClass only once and using the emplace_back for this purpose, rather than to create the instance and copy it using push_back:
using CustomClass = std::vector<int>;
std::vector<CustomClass> v;
size_t size;
int value;
std::cin >> size >> value;
CustomClass instance(size, value);
v.push_back(instance);
This however doesn't give you much benefits to compare with pushing back the r-value:
using CustomClass = std::vector<int>;
std::vector<CustomClass> v;
size_t size;
int value;
std::cin >> size >> value;
v.push_back(CustomClass(size, value));
Anyway, you need to keep in mind that both push_back and emplace_back may require reallocation of the elements, and that may be inefficient, especially if your CustomClass has no no-throwing move constructor.
Another problem could be if your class doesn't have a reasonable constructor (or the size of the values you need to pass to the constructor are almost the size of the object). In this case I'm offering you the solution to resize() and read to the back()
If the reallocation is something that you are not afraid of (for example you know the number of elements ahead of time and reserve the buffer), you can do the following:
std::vector<CustomClass> v;
v.resize(v.size() + 1);
std::cin >> v.back();
In this case you create a default value once and then read the contents.
Another solution could be to pass the std::istream to the constructor of CustomClass:
class CustomClass {
public:
CustomClass(std::istream&);
};
std::vector<CustomClass> v;
v.emplace_back(cin);
Update:
Assuming that you know nothing about the actual type of the CustomClass, the most generic (not fully generic, as it still requires default constructor to be able to be pushed with resize()) is to use resize()/back() idiom.
This is how you do it (avoids any unnecessary ctor calls, including a default one):
#include <vector>
#include <unordered_map>
#include <cstdio>
#include <iostream>
using namespace std;
//--------------------------------------------------------------------------
template <class F> struct inplacer
{
F f;
operator invoke_result_t<F&>() { return f(); }
};
template <class F> inplacer(F) -> inplacer<F>;
//--------------------------------------------------------------------------
struct S
{
S(istream&) { printf("istream ctor\n" ); }
S() { printf("ctor\n" ); }
~S() { printf("dtor\n" ); }
S(S const&) { printf("cctor\n"); }
S(S&&) { printf("mctor\n"); }
S& operator=(S const&) { printf("cop=\n"); return *this; }
S& operator=(S&&) { printf("mop=\n"); return *this; }
friend bool operator==(S const& l, S const& r) { return &l == &r; } //!! naturally, this needs proper implementation
};
template<> struct std::hash<S>
{
size_t operator()(S const&) const noexcept { return 0; } //!! naturally, this needs proper implementation
};
//--------------------------------------------------------------------------
template<class R> struct read_impl; // "enables" partial specialization
template<class R> R read(istream& is)
{
return read_impl<R>::call(is);
}
template<> struct read_impl<S>
{
static auto call(istream& is) { return S(is); }
};
template<class T> struct read_impl<vector<T>>
{
static auto call(istream& is)
{
vector<T> r; r.reserve(2); //!! naturally you'd read smth like length from 'is'
for(int i = 0; i < 2; ++i)
r.emplace_back(inplacer{[&]{ return read<T>(is); }});
return r;
}
};
template<class K, class V> struct read_impl<unordered_map<K, V>>
{
static auto call(istream& is)
{
unordered_map<K, V> r;
r.emplace( inplacer{[&]{ return read<K>(is); }}, inplacer{[&]{ return read<V>(is); }} );
return r;
}
};
//--------------------------------------------------------------------------
auto m = read<unordered_map<S, vector<S>>>(cin);
As you can see in the output -- you end up with 3 "istream ctor" calls and 3 "dtor" calls.
As for iostreams -- stay away from them if you care about performance, clarity, etc... The most ridiculous library ever.
P.S. "Partial specialization for function templates" trick is stolen from here.
I'm trying to make a sample implementation of std::unique_ptr. Here is the code:
include <iostream>
template<typename T>
class UniquePtr
{
public:
explicit UniquePtr(T * ptr)
:m_ptr(ptr)
{};
UniquePtr(UniquePtr const & other) = delete;
UniquePtr& operator=(UniquePtr const & other) = delete;
explicit UniquePtr(UniquePtr && other)
{
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
}
UniquePtr & operator=(UniquePtr && other)
{
std::cout << "Move assignment called " << std::endl;
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
}
~UniquePtr()
{
delete m_ptr;
}
T& operator*()
{
return *m_ptr;
}
T& operator->()
{
return m_ptr;
}
private:
T * m_ptr = nullptr;
};
int main()
{
UniquePtr<int> t(new int(3));
t= UniquePtr<int>(new int(4));
std::cout << *t << std::endl;
}
This code compiles and I'm able to see the value 4 in the output even after deleting the default assignment and copy constructor. What am I doing wrong?
In the assignment, move is called because the UniquePtr<int>(new int(4)) is constructing a temporary object, and in this case the compiler tries to use the move assignment if possible, else it would fall back to the copy assignment, which is deleted.
UniquePtr<int> t(new int(3));
t= UniquePtr<int>(new int(4)); // move assignment because temporary
auto k = t; // copy assignment since t is not temporary and so does not compile.
As commented, the move assignment is not returning *this, you should enable all warnings. Also the last operator-> has a syntax error, it returns a pointer but expects a reference.
Additionally, your code has a major issue, in case of exceptions you could have a memory leak. Suppose you write something like:
class Banana
{ ... }
void eatBanana(UniquePtr<Banana> banana, int amountToEat);
int computeAmount();
eatBanana(UniquePtr<Banana>(new Banana(3)), computeAmount());
If, for any reason, the computeAmount() function throws an exception, then the memory allocated by new could never be released, because computeAmount() could be executed between new and the constructor of the UniquePtr. For this reason, we normally use std::make_unique().
You should implement your own version of make_unique() and use it, it's trival, see here: https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
Here more information about the issues and the solution:
https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch04.html
I can't figure out how push_back(const value_type& val) exactly works, in docs it says about val that
val is Value to be copied (or moved) to the new element ...
How it can be copied when it takes val by reference ?
Will that copying ever call the copy constructor of val ?
and what's exactly happening here ?
#include <iostream>
#include <vector>
using namespace std;
struct x
{
x(int v = 0) : v(v) {}
int v;
};
vector<vector<x>> parts;
void fillParts()
{
vector<x> values = { x(1), x(2), x(3) };
parts.push_back(values);
}
int main()
{
fillParts();
parts[0][0].v = -123;
cout << parts[0][0].v; // -123
return 0;
}
this runs with no erros,
is parts[0] is a reference to local vector values or a copy ?
if it is a reference shouldn't it at least give some warnings saying that your accessing and modifying local objects of freed stack ?
How it can be copied when it takes val by reference?
Think of a copy constructor.
It takes parameter by reference, and it performs copying perfectly.
class Bar
{
public:
Bar(const Bar & rhs); // by reference, to copy.
};
Will that copying ever call the copy constructor of val ?
Copy operation uses copy constructor.
You can actually see if it's copied, or moved by providing user-defined constructors.
struct x
{
public:
x(const x & rhs)
{
// Some copy operation.
std::cout << "Copied" << std::endl;
}
x(x && rhs)
{
// Some move operation.
std::cout << "Moved" << std::endl;
}
};
You can try this
class A
{
public:
A() {}
A(const A&) { cout << "copy cons" << endl; }
A& operator= (A &&) { cout << "move" << endl; };
A& operator= (const A &) { cout << "copy" << endl; };
};
vector<A> parts;
void fillParts()
{
A a;
parts.push_back(a);
}
int main()
{
fillParts();
return 0;
}
I got copy cons called in both debug and release builds.