The code is following:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct A {
A(int i = -1): i_(i) {
wcout << "Constructor: i = " << i_ << endl;
}
A(A const &a) {
wcout << "Copy constructor: i = " << i_ << " a.i = " << a.i_ << endl;
*this = a;
}
~A() { wcout << "Destructor: i = " << i_ << endl; }
A &operator=(A const& a) {
wcout << "Copy assignment operator: i = " << i_ << " a.i = " << a.i_ << endl;
i_ = a.i_;
return *this;
}
bool operator==(A const& rhs) { return i_ == rhs.i_; }
int get() { return i_; }
private:
int i_;
};
int wmain() {
A a[] = {1, 2, 3, 2, 4, 5};
vector<A> v(a, a + sizeof a/sizeof a[0]);
wcout << "==== Just before remove ====" << endl;
remove(v.begin(), v.end(), 2);
wcout << "==== Just after remove ====" << endl;
return 0;
}
Output:
==== Just before remove ====
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 2 a.i = 3
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 3 a.i = 4
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 2 a.i = 5
==== Just after remove ====
The question is: why destructor is called 6 times while remove() was running? I need this mess to be clarified.
NOTE: execute this code on your system, please, before answering.
NOTE: MSVCPP 11
The question is: why destructor is called 6 times while remove() was
running?
In summary, the destructor calls have to do with the 2 getting implicitly converted to A by remove(). Every time the result of such an implicit conversion goes out of scope, A's destructor gets called.
The reason for those implicit conversions is that remove() needs to compare every element of a to 2. The only way to do this is by calling A::operator==(const A&):
bool operator==(A const& rhs) { ... }
Since rhs is of type const A&, the compiler:
calls A(int) to convert the 2 to an A;
calls operator==(const A&);
calls A::~A() to destroy the temporary.
The latter are the destructor calls that you're seeing.
If you were to add the following comparison operator to A, you'll see those destructor calls disappear:
bool operator==(int rhs) { return i_ == rhs; }
Alternatively, if you were to call remove() like so, you'll see all bar one destructor calls disappear:
remove( v.begin(), v.end(), A(2) );
Finally, if you were to make A::A(int) explicit, the compiler would not allow you to call remove() with 2 as the last argument (you'd have to call it with A(2)).
The question is: why destructor is called 6 times while remove() was
running?
Because std::remove just reorders the elements, but doesn't remove anything from the vector. In the process of reordering, some elements are copy-constructed.
Next code shows in details what happens :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct A {
A(int i = -1): i_(i) {cout << "I was created" << i_ << endl;}
~A() {
cout << "I was destroyed" << i_ << endl;
}
A(const A& o):i_(o.i_)
{
cout << "I was copied" << i_ << endl;
}
A& operator=(const A& o)
{
i_=o.i_;
cout << "I was assigned" << i_ << endl;
return *this;
}
int get() { return i_; }
bool operator==(A const& rhs) { return i_ == rhs.i_; }
private:
int i_;
};
int main() {
std::cout<<"start"<<std::endl;
A a[] = {1, 2, 3, 2, 4, 5};
std::cout<<"creating"<<std::endl;
vector<A> v(a, a + sizeof a/sizeof a[0]);
std::cout<<"removing"<<std::endl;
remove( v.begin(), v.end(), 2 );
std::cout<<"end"<<std::endl;
}
The remove is placing "removed" elements at the end of the vector.
std::remove is declared as
template<typename ForwardIterator, typename Tp>
ForwardIterator remove(ForwardIterator first, ForwardIterator last,
const Tp& value);
In your usage, the third argument (2) is deduced as int. Because an int variable is not directly comparable against an A object, first a temporary has to be constructed for every
if (*it == value) ...
At the end of the comparison, the temporary is destroyed.
(All in all, you are having a hidden performance problem, resulting from having non-explicit single-argument, a.k.a. conversion constructor.)
Well it's quite simple really; you only need to understand what std::remove does and how it does it. Hint: it does not remove elements from the vector. It moves then around instead, to the back of your collection. Moving an element in vector involves destroying original element. So this is where (part of) your destructor calls is coming from.
The other part is coming from temporary objects - since you passed an int (and not an instance of struct A) as a last parameter to std::remove, an instance of A has to be constructed for the purpose of comparison. If you want to force a little more discipline to your code, try to make it a habit to prefix one-parameter constructors with keyword explicit. It's very efficient at banishing such temporary objects. You will then have to create comparison object explicitly:
remove(v.begin(), v.end(), A(2));
Related
For the following example, why the vector move operation is not triggered? How do I know when I should explicitly use a move operator?
#include <iostream>
#include <vector>
using namespace std;
class Test {
public:
Test() {
std::cout << " default " << std::endl;
}
Test(const Test& o) {
std::cout << " copy ctor " << std::endl;
}
Test& operator=(const Test& o) {
std::cout << " copy assign " << std::endl;
return *this;
}
Test(Test&& o) {
std::cout << " move ctor" << std::endl;
}
Test& operator=(Test&& o) {
std::cout << " move assign " << std::endl;
return *this;
}
};
int main()
{
std::cout << " vector: " << std::endl;
std::vector<Test> p;
p = {Test()}; // expect vector move here since the RHS is temporary.
std::cout << std::endl;
std::cout << " single value " << std::endl;
Test tt;
tt = Test();
}
Output:
vector:
default
copy ctor
single value
default
default
move assign
I was under the impression that when we assign a temporary variable to a lvalue (the single value case in the example), it would trigger a move operation if it exists. Seems that I was wrong and my understanding was overly simplified, I need to carefully check case by case to ensure there's no redundant copy.
std::vector has an assignment operator that takes an std::initializer_list:
vector& operator= (initializer_list<value_type> il);
So when you wrote p = {Test()}; you're actually using the above assignment operator.
Now why a call to the copy constructor is made can be understood from dcl.init.list, which states:
An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list object is constructed to refer to that array.
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 have the following sample code, and the copy assignment is doing something I don't want - it's first constructing the new samp(6), then copying it to z, then destroying the new samp(6) it built. Is there a way to change the constructors such that = acts like a pointer, destroying the originally constructed samp(5) and replacing it with the new samp(6), calling the destructor on samp(5), rather than samp(6)?
#include <iostream>
class samp
{
public:
samp(int a)
{
m_a = a;
std::cout << "cons" <<m_a << std::endl;
}
int m_a;
samp(const samp& other)
{
std::cout << "copy" << m_a << std::endl;
m_a = other.m_a;
}
samp& operator= (const samp& other)
{
std::cout << "assg" << m_a << std::endl;
samp* z =new samp(other.m_a);
return *z;
}
~samp()
{
std::cout << "dest" <<m_a<< std::endl;
}
};
int main()
{
samp z(5);
z = samp(6);
std::cout << z.m_a << std::endl;
return 0;
}
Maybe pointer semantics is what you want:
#include <memory>
// ...
auto z = std::make_unique<samp>(5);
z = std::make_unique<samp>(6); // dest5
std::cout << z->m_a << '\n'; // 6
Although if you are coming to C++ from a language where object names are object references, it may be better to get used to C++ value semantics instead of trying to replicate object references :)
operator= is a member of your object so the this pointer is available. Also, assignment means the target of the assignment should change. Your version is creating a new object, but leaving the target alone. See if this does what you want:
samp& operator= (const samp& other)
{
m_a = other.m_a;
return *this;
}
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.
I'm trying to get the deep knowledge about how should I write my copy and move constructors and assignment operators.
In Bjarne Stroustrup's "The C++ Programming Language - 2013" I see the following example of move constructor and move assignment:
template<class T, class A>
vector_base<T,A>::vector_base(vector_base&& a)
: alloc{a.alloc},
elem{a.elem},
space{a.space},
last{a.space}
{
a.elem = a.space = a.last = nullptr; // no longer owns any memory
}
template<class T, class A>
vector_base<T,A>::& vector_base<T,A>::operator=(vector_base&& a)
{
swap(∗this,a);
return *this;
}
(Side note: there seems to be a typo in the book: ::& should be just &, right?)
I suspected it should cause endless recursion, since std::swap() calls move assignment operator:
template<typename T>
void swap(T& lhs, T& rhs)
{
auto temp(lhs);
lhs = std::move(rhs);
rhs = std::move(temp);
}
I've checked it, here's very simple program:
#include <iostream>
using namespace std;
class TestA {
int x;
public:
TestA(int x = 0) : x(x) {
cout << "TestA value ctor " << x << "\n";
}
~TestA() {
cout << "TestA dtor " << x << "\n";
}
TestA(const TestA &a) : x(a.x) {
cout << "TestA copy ctor " << x << "\n";
}
TestA(TestA &&a) : x(a.x) {
cout << "TestA move ctor " << x << "\n";
}
TestA operator=(const TestA &a) {
x = a.getX();
cout << "TestA copy assignment " << x << " = " << a.getX() << "\n";
return *this;
}
TestA &operator=(TestA &&a) {
cout << "TestA move assignment " << x << " = " << a.getX() << "\n";
swap(*this, a);
return *this;
}
int getX() const {
return this->x;
}
};
int main(void) {
TestA a{0};
TestA b{1};
{
TestA c{2};
a = move(c);
}
}
Which produces the following output, so I was right about endless recursion:
TestA value ctor 0
TestA value ctor 1
TestA value ctor 2
TestA move assignment 0 = 2
TestA move ctor 0
TestA move assignment 0 = 2
TestA move ctor 0
TestA move assignment 0 = 2
TestA move ctor 0
...
...
Do I miss something? How can I use swap() inside move assignment?
You're not missing anything, and the accepted answer is wrong. Stroustrup's book is simply bad. There is no free-standing swap() anywhere in chapter 13; it's strongly implied that this swap() is std::swap() (just like copy() is std::copy(), etc.). It's just a bug. Welcome to C++.
What you are missing is that Stroustroup provides a free-function swap(TestA&, TestA&) in the same namespace as the class.
Also, he does not call it as std::swap (neither does your code), but uses an unqualified id and injection of std::swap into the namespace with using ::std::swap;.
Which means the generic version provided by the standard is not used.
At least that is how it should be. Seems that free-standing swap() is really missing. Ouch.