move assignment from newly constructed object to *this in a member function - c++

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.

Related

vector move operation vs element move operation

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.

Is it possible to avoid copy-constructor call in C++

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.

How to utilize a public constant reference to a private (struct) member for code encapsulation

In my buggy class, I'm only exposing a constant version of MyStruct, experimenting with initializing a constant reference to the private member in an initializer list.
The program I'm debugging is functionally equivalent to the following:
#include <iostream>
#include <optional>
struct MyStruct {
int member = 1;
};
class MyType {
MyStruct struct_member_;
public:
MyType() : struct_member(struct_member_) {}
MyType& operator =(const MyType& other_side) {
struct_member_ = other_side.struct_member_;
return *this;
};
const MyStruct& struct_member;
void test() const {
std::cout << "Why isn't " << &struct_member << " the same as " << &struct_member_ << std::endl;
}
};
int main()
{
std::optional<MyType> my = MyType();
my.value().test();
std::optional<MyType> yours = MyType();
yours.value().test();
my = yours;
my.value().test();
my = MyType();
my.value().test();
return 0;
}
For this program, here's the output:
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
Why isn't 0x7ffeefbff868 the same as 0x7ffeefbff878
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
However, in the program I'm debugging, struct_member_ and struct_member are getting desynchronized (either the const reference struct_member is getting assigned to a different place in memory-- except it's const, or it's loosing track with an updated struct_member_-- except its memory address shouldn't change?) and I'm not sure which or why.
Any ideas on what could lead to this happening, or tips to make this pattern work with non-POD types? (I plan to be transitioning to method-based accessors anyway, since this experiment seems to be failing.)
Turns out optionals and copy constructors were key to all of this. The copy constructor was defaulting and I didn't realize it. Funny, because the compiler warned that I needed a non-default operator=, but didn't afford me the same warning for the copy constructor.
class MyType {
MyStruct struct_member_;
public:
MyType() : struct_member(struct_member_) {
std::cout << "constructor" << std::endl;
}
MyType(const MyType& other) : struct_member(struct_member_) {
std::cout << "copy constructor " << std::endl;
struct_member_ = other.struct_member_;
};
MyType& operator =(const MyType& other_side) {
std::cout << "assignment operator called" << std::endl;
struct_member_ = other_side.struct_member_;
return *this;
};
const MyStruct& struct_member;
void test() const {
std::cout << "Why isn't " << struct_member.member << ": " << &struct_member << " the same as " << &struct_member_ << std::endl;
}
};
Yields the correct output:
constructor
copy constructor
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
constructor
copy constructor
Why isn't 0x7ffeefbff878 the same as 0x7ffeefbff878
assignment operator called
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
constructor
assignment operator called
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
Altogether, it'd be less hassle just to expose getters as methods.
Either delete the copy constructor (because the default behavior binds the reference to the "wrong" thing in this case):
MyType(MyType const&) = delete;
Or provide a copy constructor that does the right thing, by binding the public const accessor to the self-same object:
MyType(MyType const& other)
: struct_member_{other.struct_member_}
, struct_member(struct_member_) {}

Change constructor order of operations?

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;
}

C++: STL: vector: remove: destructor calls

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));