Similar questions like mine have already been discussed in this community (there are several posts, like this, this, this, this and this), but the most interesting one (for what I would like to discuss here) is this, though it does not really solve my problem. What I would like to discuss is the following warning:
warning: defaulted move assignment for ‘UG’ calls a non-trivial move assignment operator for virtual base ‘G’.
In the last post mentioned, one user answered that this warning is saying that the base class can be moved twice and so
The second move assignment is from an already moved from object, which
could cause the contents from the first move assignment to be
overwritten.
I understand that this is a problem and it has better be avoided. Now, I have several classes inheriting from a purely virtual base class. Multiple inheritance is also involved, and is represented in the MWE below. What I would like to have is the possibility of using the move constructor and the move assignment operator whenever needed, so that I can do
T t3;
T t2 = std::move(t1);
t3 = std::move(t2);
without worrying about memory leaks, and everything being moved correctly. Presently, T t2 = std::move(t1); works fine, but t3 = std::move(t2); does not. I made a MWE, which represents very well my actual code, and I'm quite convinced that a solution for the MWE will also be a solution for my code. The MWE is:
class G {
public:
G() = default;
G(G&&) = default;
G(const G&) = default;
virtual ~G() = default;
G& operator= (G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
return *this;
}
G& operator= (const G&) = default;
virtual void asdf() = 0; // abstract function to force complexity
string mem_G;
};
class UG : virtual public G {
public:
UG() = default;
UG(UG&& u) = default;
UG(const UG&) = default;
virtual ~UG() = default;
UG& operator= (UG&&) = default;
UG& operator= (const UG&) = default;
void asdf() { mem_G = "asdf"; }
string mem_UG;
};
class T : virtual public G {
public:
T() = default;
T(T&& t) = default;
T(const T&) = default;
virtual ~T() = default;
T& operator= (T&&) = default;
T& operator= (const T&) = default;
virtual void qwer() = 0;
string mem_T;
};
class FT : public UG, virtual public T {
public:
FT() = default;
FT(FT&& f) = default;
FT(const FT&) = default;
virtual ~FT() = default;
FT& operator= (FT&&) = default;
FT& operator= (const FT&) = default;
friend ostream& operator<< (ostream& os, const FT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_FT: " << r.mem_FT;
return os;
}
void qwer() { mem_FT = "zxvc"; }
string mem_FT;
};
Using the classes in the example, the function
void test() {
FT c1;
c1.mem_G = "I am G";
c1.mem_UG = "I am UG";
c1.mem_T = "I am T";
c1.mem_FT = "I am FT";
cout << "c1" << endl;
cout << c1 << endl;
cout << "Move constructor" << endl;
FT c2 = std::move(c1);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Move assignment operator" << endl;
c1 = std::move(c2);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
produces the output (without the comments, which I added for a better understanding of the output)
c1
mem_G: I am G
mem_UG: I am UG
mem_T: I am T
mem_FT: I am FT
Move constructor // correct move of 'c1' into 'c2'
c1
mem_G:
mem_UG:
mem_T:
mem_FT:
c2
mem_G: I am G
mem_UG: I am UG
mem_T: I am T
mem_FT: I am FT
Move assignment operator // moving 'c2' into 'c1' using the move operator will move G's memory twice
G& G::operator=(G&&) // moving once ...
G& G::operator=(G&&) // moving twice ... (not really, because that is not implemented!)
c1
mem_G:
mem_UG: I am UG
mem_T: I am T
mem_FT: I am FT
c2
mem_G: I am G // this memory hasn't been moved because G::operator(G&&)
mem_UG: // does not implement the move.
mem_T:
mem_FT:
Notice how mem_G in its last appearance kept its value in c2. In case I defaulted G& operator=(G&&) instead of defining it, the result only differs in that line:
c2
mem_G: // this memory has been moved twice
Question How do I implement the move assignment operators (and the move constructors, in case it is needed) within this inheritance structure so that both move the memory only once? Is it possible to have such code without the warning above?
Thanks in advance.
Edit This problem has been solved thanks to this answer. I thought people would find it useful to see a complete proposal of a solution, so I'm adding an extended version of the MWE with two more classes so that it is a little bit more complicated. Also, there is the main function so the classes can be tested. Finally, I would like to add that valgrind does not complain about memory leaks when executing a debug compilation of the code.
Edit I completed the example following the rule of 5, just like one of the users who commented on this answer pointed out, and I thought I would update the answer. The code compiles with no warning with the flags -Wall -Wpedantic -Wshadow -Wextra -Wconversion -Wold-style-cast -Wrestrict -Wduplicated-cond -Wnon-virtual-dtor -Woverloaded-virtual and the execution with valgrind does not produce any errors. I've also added couts with the __PRETTY_FUNCTION__ macro so that anyone who wishes to test the code can see the trace of function calls.
#include <functional>
#include <iostream>
#include <string>
using namespace std;
class G {
public:
G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = "empty";
}
G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
}
G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
}
virtual ~G() { }
G& operator= (const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
return *this;
}
G& operator= (G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
return *this;
}
friend ostream& operator<< (ostream& os, const G& r) {
os << " mem_G: " << r.mem_G;
return os;
}
virtual void asdf() = 0;
string mem_G;
protected:
void copy_full_G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = g.mem_G;
}
void move_full_G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = std::move(g.mem_G);
}
};
class UG : virtual public G {
public:
UG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_UG = "empty";
}
UG(const UG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
}
UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
}
virtual ~UG() { }
UG& operator= (const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
return *this;
}
UG& operator= (UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const UG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_UG;
protected:
void copy_full_UG(const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_UG = u.mem_UG;
}
void move_full_UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_UG = std::move(u.mem_UG);
}
};
class DG : virtual public G {
public:
DG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_DG = "empty";
}
DG(const DG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
}
DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
}
virtual ~DG() { }
DG& operator= (const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
return *this;
}
DG& operator= (DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const DG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_DG;
protected:
void copy_full_DG(const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_DG = u.mem_DG;
}
void move_full_DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_DG = std::move(u.mem_DG);
}
};
class T : virtual public G {
public:
T() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = "empty";
}
T(const T& t) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
}
T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
}
virtual ~T() { }
T& operator= (const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
return *this;
}
T& operator= (T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
return *this;
}
friend ostream& operator<< (ostream& os, const T& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_T: " << r.mem_T;
return os;
}
virtual void qwer() = 0;
string mem_T;
protected:
// Copy *only* T members.
void copy_only_T(const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = t.mem_T;
}
// Move *only* T members.
void move_only_T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
// if we moved G's members too then we
// would be moving G's members twice!
//move_full_G(std::move(static_cast<G&>(t)));
mem_T = std::move(t.mem_T);
}
};
class FT : public UG, virtual public T {
public:
FT() : T(), UG(){
cout << __PRETTY_FUNCTION__ << endl;
mem_FT = "empty";
}
FT(const FT& f) : G(), T(), UG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
}
FT(FT&& f) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_FT(std::move(static_cast<FT&>(f)));
}
virtual ~FT() { }
FT& operator= (const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
return *this;
}
FT& operator= (FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign FT members
move_full_FT(std::move(static_cast<FT&>(other)));
return *this;
}
friend ostream& operator<< (ostream& os, const FT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_FT: " << r.mem_FT;
return os;
}
void qwer() { mem_FT = "zxvc"; }
string mem_FT;
protected:
void copy_full_FT(const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(f);
copy_only_T(f);
mem_FT = f.mem_FT;
}
void move_full_FT(FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign UG members and also the base class's members
move_full_UG(std::move(static_cast<UG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_FT = std::move(other.mem_FT);
}
};
class RT : public DG, virtual public T {
public:
RT() : T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
mem_RT = "empty";
}
RT(const RT& f) : G(), T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(f);
}
RT(RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_RT(std::move(static_cast<RT&>(r)));
}
virtual ~RT() { }
RT& operator= (const RT& r) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(r);
return *this;
}
RT& operator= (RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign RT members
move_full_RT(std::move(static_cast<RT&>(r)));
return *this;
}
friend ostream& operator<< (ostream& os, const RT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_RT: " << r.mem_RT;
return os;
}
void qwer() { mem_RT = "zxvc"; }
string mem_RT;
protected:
void copy_full_RT(const RT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(f);
copy_only_T(f);
mem_RT = f.mem_RT;
}
void move_full_RT(RT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign DG members and also the base class's members
move_full_DG(std::move(static_cast<DG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_RT = std::move(other.mem_RT);
}
};
template<class C> void test_move(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
init_C(c1);
cout << "Initialise c1" << endl;
cout << c1 << endl;
cout << "Move constructor: 'c2 <- c1'" << endl;
C c2 = std::move(c1);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Move assignment operator: 'c1 <- c2'" << endl;
c1 = std::move(c2);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C> void test_copy(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
cout << "Initialise c1" << endl;
init_C(c1);
cout << c1 << endl;
cout << "Copy constructor: 'c2 <- c1'" << endl;
C c2 = c1;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Copy assignment operator: 'c1 <- c2'" << endl;
c1 = c2;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C>
void test(const string& what, const function<void (C&)>& init_C) {
cout << "********" << endl;
cout << "** " << what << " **" << endl;
cout << "********" << endl;
cout << "----------" << endl;
cout << "-- MOVE --" << endl;
cout << "----------" << endl;
test_move<C>(init_C);
cout << "----------" << endl;
cout << "-- COPY --" << endl;
cout << "----------" << endl;
test_copy<C>(init_C);
}
int main() {
test<UG>(
"UG",
[](UG& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
}
);
test<DG>(
"DG",
[](DG& d) -> void {
d.mem_G = "I am G";
d.mem_DG = "I am DG";
}
);
test<FT>(
"FT",
[](FT& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
u.mem_T = "I am T";
u.mem_FT = "I am FT";
}
);
test<RT>(
"RT",
[](RT& u) -> void {
u.mem_G = "I am G";
u.mem_DG = "I am DG";
u.mem_T = "I am T";
u.mem_RT = "I am RT";
}
);
}
The problem is that FT's FT& operator= (FT&&) = default; is essentially:
FT& operator=(FT&& other) {
// Move-assign base classes
static_cast<UG&>(*this) = std::move(static_cast<UG&>(other)); // Also move-assigns G
// other.mem_G is now empty after being moved
static_cast<T&>(*this) = std::move(static_cast<T&>(other)); // Also move-assigns G
// this->mem_G is now empty
// Move-assign members
mem_FT = std::move(other.mem_FT);
}
(Though not exactly. A compiler is allowed to be smart and only move from a virtual base class once, but that doesn't happen with gcc and clang at least)
Where the single base class subobject G is moved into from other twice (through the two move assigns). But other.mem_G is empty after the first move, so it will be empty after the move assign.
The way to deal with this is to make sure that the virtual base is only move-assigned once. This can easily be done by writing something like this:
FT& operator=(FT&& other) noexcept {
// Also move-assigns `G`
static_cast<T&>(*this) = std::move(static_cast<T&>(other));
// Move-assign UG members without UG's move assign that moves `G`
mem_UG = std::move(other.mem_UG);
// Move-assign FT members
mem_FT = std::move(other.mem_FT);
}
With private members or a more complicated move-assign, you might want to make a protected move_only_my_members_from_this_type_and_not_virtual_bases(UG&&) member function
You can also fix this by not generating a default move-assign operator, making the base class be copied twice instead of becoming empty, for a potential performance hit.
Related
I wrote a generic class for handling and executing a function pointer. This is a simplified equivalent of std::function and std::bind. To handle member functions I use cast to internal EventHandler::Class type. Question: is it ok to cast it that way? Will it work in all cases when invoking handled function?
template <typename ReturnType, typename... Arguments>
class EventHandler
{
class Class {};
ReturnType (Class::*memberFunction)(Arguments...) = nullptr;
union {
Class *owner;
ReturnType(*function)(Arguments...) = nullptr;
};
public:
EventHandler() = default;
EventHandler(EventHandler &&) = default;
EventHandler(const EventHandler &) = default;
EventHandler &operator=(EventHandler &&) = default;
EventHandler &operator=(const EventHandler &) = default;
EventHandler(ReturnType (*function)(Arguments...)) :
function(function)
{
}
template <typename Owner>
EventHandler(Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...)) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
template <typename Owner>
EventHandler(const Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...) const) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
ReturnType operator()(Arguments... arguments)
{
return memberFunction ?
(owner ? (owner->*memberFunction)(arguments...) : ReturnType()) :
(function ? function(arguments...) : ReturnType());
}
};
The implementation provides handle for a global function, a member function and a const member function. Obviously there is volatile and const volatile that is not show here for clarity.
EDIT
All the code below is just a representation of all of kinds of supported functions.
class Object
{
public:
double y = 1000;
Object() = default;
Object(double y) : y(y) {}
static void s1(void) { std::cout << "s1()" << std::endl; }
static void s2(int a) { std::cout << "s2(a:" << 10 + a << ")" << std::endl; }
static void s3(int a, float b) { std::cout << "s3(a:" << 10 + a << ", b:" << 10 + b << ")" << std::endl; }
static int s4(void) { std::cout << "s4(): "; return 10 + 4; }
static Object s5(int a) { std::cout << "s5(a:" << 10 + a << "): "; return Object(10 + 5.1); }
static float s6(int a, Object b) { std::cout << "s6(a:" << 10 + a << ", b:" << 10 + b.y << "); "; return 10 + 6.2f; }
void m1(void) { std::cout << "m1()" << std::endl; }
void m2(int a) { std::cout << "m2(a:" << y + a << ")" << std::endl; }
void m3(int a, float b) { std::cout << "m3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int m4(void) { std::cout << "m4(): "; return ((int) y) + 4; }
Object m5(int a) { std::cout << "m5(a:" << y + a << "): "; return Object(y + 5.1); }
float m6(int a, Object b) { std::cout << "m6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
void c1(void) const { std::cout << "c1()" << std::endl; }
void c2(int a) const { std::cout << "c2(a:" << y + a << ")" << std::endl; }
void c3(int a, float b) const { std::cout << "c3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int c4(void) const { std::cout << "c4(): "; return ((int) y) + 4; }
Object c5(int a) const { std::cout << "c5(a:" << y + a << "): "; return Object(y + 5.1); }
float c6(int a, Object b) const { std::cout << "c6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
};
void f1(void) { std::cout << "f1()" << std::endl; }
void f2(int a) { std::cout << "f2(a:" << a << ")" << std::endl; }
void f3(int a, float b) { std::cout << "f3(a:" << a << ", b:" << b << ")" << std::endl; }
int f4(void) { std::cout << "f4(): "; return 4; }
Object f5(int a) { std::cout << "f5(a:" << a << "): "; return Object(5.1); }
float f6(int a, Object b) { std::cout << "f6(a:" << a << ", b:" << b.y << "); "; return 6.2f; }
Here is the usage example for all of the above functions
int main()
{
std::cout << "=== Global functions" << std::endl;
EventHandler ef1(f1); ef1();
EventHandler ef2(f2); ef2(2);
EventHandler ef3(f3); ef3(3, 3.1f);
EventHandler ef4(f4); std::cout << ef4() << std::endl;
EventHandler ef5(f5); std::cout << ef5(5).y << std::endl;
EventHandler ef6(f6); std::cout << ef6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member static functions" << std::endl;
EventHandler es1(Object::s1); es1();
EventHandler es2(Object::s2); es2(2);
EventHandler es3(Object::s3); es3(3, 3.1f);
EventHandler es4(Object::s4); std::cout << es4() << std::endl;
EventHandler es5(Object::s5); std::cout << es5(5).y << std::endl;
EventHandler es6(Object::s6); std::cout << es6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member functions" << std::endl;
Object object(20);
EventHandler em1(&object, &Object::m1); em1();
EventHandler em2(&object, &Object::m2); em2(2);
EventHandler em3(&object, &Object::m3); em3(3, 3.1f);
EventHandler em4(&object, &Object::m4); std::cout << em4() << std::endl;
EventHandler em5(&object, &Object::m5); std::cout << em5(5).y << std::endl;
EventHandler em6(&object, &Object::m6); std::cout << em6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member const functions" << std::endl;
const Object constObject(30);
EventHandler ec1(&constObject, &Object::c1); ec1();
EventHandler ec2(&constObject, &Object::c2); ec2(2);
EventHandler ec3(&constObject, &Object::c3); ec3(3, 3.1f);
EventHandler ec4(&constObject, &Object::c4); std::cout << ec4() << std::endl;
EventHandler ec5(&constObject, &Object::c5); std::cout << ec5(5).y << std::endl;
EventHandler ec6(&constObject, &Object::c6); std::cout << ec6(6, Object(6.1)) << std::endl;
system("pause");
return 0;
}
Finally - to the point - here an example that shows how much easier in use is the EventHandler I prepared when compared to std::function interface. And actually the reason of such approach.
EventHandler<float, int, Object> example;
example = f6;
example(7, Object(7.1));
example = EventHandler(&object, &Object::m6);;
example(8, Object(8.1));
It’s undefined behavior to call a function through a function pointer(-to-member) of a different type. (Some practical reasons for this rule are that the object’s address might need to be adjusted to call a member function of a base class or that a vtable might be involved.) You can use type erasure to allow calling member functions on objects of different types (which is what std::bind does), or you can (restrict to member functions and) add the class type as a template parameter.
Of course, the usual answer is to just use std::function with a lambda that captures the object in question and calls whatever member function. You can also take the C approach and define various functions with a void* parameter that cast that parameter to a known class type and call the desired member function.
I am trying to pass literal to a perfect forwarding setter, but this results in object construction, movement and destruction instead. I am looking for a more efficient way to do this.
struct TStruct {
TStruct(int va) : a(va) {
std::cout << "- TStruct(" << va << ")" << std::endl;
}
TStruct(const TStruct& other) :
a(other.a)
{
std::cout << "- TStruct(const TStruct& )" << std::endl;
}
TStruct(TStruct&& other) :
a(std::exchange(other.a, 0))
{
std::cout << "- TStruct(const TStruct&&)" << std::endl;
}
TStruct& operator=(const TStruct& rhs) {
std::cout << "- TStruct operator= const& " << std::endl;
// check for self-assignment
if(&rhs == this) return *this;
a = rhs.a;
return *this;
}
TStruct& operator=(TStruct&& rhs) {
std::cout << "- TStruct operator=&&" << std::endl;
// check for self-assignment
if(&rhs == this) return *this;
a = std::exchange(rhs.a, 0);
return *this;
}
~TStruct() {
std::cout << "~ TStruct() destructor with " << a << std::endl;
}
int a = 1;
};
struct TPerfectForward {
template<class T>
TPerfectForward(T&& tsv) : ts(std::forward<T>(tsv)) {}
template<class T>
void set(T&& tsv) { ts = std::forward<T>(tsv); }
TStruct ts;
};
std::cout << "TPerfectForward (5)" << std::endl;
TPerfectForward pf(5);
std::cout << "TPerfectForward set(4)" << std::endl;
pf.set(4);
This gives following results with gcc 7.4.0 on Ubuntu:
TPerfectForward (5);
- TStruct(5)
TPerfectForward set(4)
- TStruct(4)
- TStruct operator=&&
~ TStruct() destructor with 0
I would like these results better:
TPerfectForward (5)
- TStruct(5)
TPerfectForward set(4)
- TStruct(4)
Is there a way to avoid calling move operator and destructor when perfect forwarding to a function?
I am new to programming. sorry for my bad english.
I have tried to use rvalue as initialiser to initial objects.
So, according to the code, it would print out what are the used constructor and assignment operator.
But turned out object "what2" and "what3", those don't print out anything.
here is the code:
#include <iostream>
using namespace std;
class X{
public:
int x;
X()= default;
X(int num):x{num}{}
X(const X& y){
x = y.x;
std::cout << "copy constructor" << std::endl;
std::cout << x << std::endl;
}
X& operator=(const X& d){
x = d.x;
std::cout << "copy assignment" << std::endl;
return *this;
}
X(X&& y){
x = y.x;
std::cout << "move constructor" << std::endl;
}
X& operator=(X&& b){
x = b.x;
std::cout << "move assignment" << std::endl;
return *this;
}
};
X operator +(const X& a,const X& b){
X tmp{};
tmp.x = a.x +b.x;
return tmp;
}
int main(int argc, const char * argv[]) {
X a{7} , b{11};
X what1;
cout << "---------------" << endl;
what1 = a+b;
cout << "---------------" << endl;
X what2{a+b};
cout << "---------------" << endl;
X what3 = a+b;
cout << "---------------" << endl;
std::cout << what1.x << std::endl;
std::cout << what2.x << std:: endl;
std::cout <<what3.x << std::endl;
return 0;
}
the output is:
---------------
move assignment
---------------
---------------
---------------
18
18
18
Program ended with exit code: 0
only "what1" uses assignment properly.
so, how can i use rvalue to initial an object? and using operator= to initial an object?
thank you very much.
Your code could result in move operations being used, but your compiler has chosen to elide those moves and allocate the return of operator+ directly at the call site. You can see this happening if you disable copy elision in your compiler (-fno-elide-constructors in GCC or Clang).
Your move constructor and assignment operator will be successfully used in contexts in which copy elision is not permitted, such as this:
X what2 { std::move(what1) }; //can't elide copy, move constructor used
The following code triggers more of your constructors/operators, check it out to see which trigger in what cases
#include <iostream>
using namespace std;
class X{
public:
int x;
X()
{
x = 0;
std::cout << "constructor" << std::endl;
}
X(int num):x{num}
{
std::cout << "list initilizer" << std::endl;
}
X(const X& y){
x = y.x;
std::cout << "copy constructor" << std::endl;
}
X& operator=(const X& d){
x = d.x;
std::cout << "copy assignment" << std::endl;
return *this;
}
X(X&& y){
x = y.x;
std::cout << "move constructor" << std::endl;
}
X& operator=(X&& b){
x = b.x;
std::cout << "move assignment" << std::endl;
return *this;
}
};
X operator +(const X& a,const X& b){
X tmp{};
tmp.x = a.x +b.x;
return tmp;
}
int main(int argc, const char * argv[]) {
X a{7} , b{11};
cout << "---------------" << endl;
X what1;
cout << "---------------" << endl;
what1 = a+b;
cout << "---------------" << endl;
X what2(a+b);
cout << "---------------" << endl;
X what3 = X(a);
cout << "---------------" << endl;
X what4 = X();
cout << "---------------" << endl;
X what5 = std::move(what1);
cout << "---------------" << endl;
what5 = std::move(what1);
cout << "---------------" << endl;
what5 = what1;
cout << "---------------" << endl;
return 0;
}
GCC provides the -fno-elide-constructors option to disable copy-elision. if you want to elide the copy-elision then use flag -fno-elide-constructors. Refer https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization/27916892#27916892 for more detail
This question already has answers here:
Overriding of operator<< in c++
(2 answers)
Closed 7 years ago.
I want to implement a composite pattern where I can print the contents using std::cout
When I print, the base class insertion operator (operator<<) is used instead of the most derived type. How can I cause the most derived type operator to be used?
Example:
#include <iostream>
#include <memory>
using namespace std;
class Base {
int i;
public:
Base(int _i) : i(_i) {}
virtual ~Base() {}
friend inline ostream& operator<<(ostream& os, Base& value) { return os << "i: " << value.i; }
friend inline ostream& operator<<(ostream& os, shared_ptr<Base> value) { return os << "i: " << value->i; }
};
class Derived : public Base {
int x;
public:
Derived(int _x) : Base(2*_x), x(_x) {}
friend inline ostream& operator<<(ostream& os, Derived& value) { return os << "x: " << value.x; }
friend inline ostream& operator<<(ostream& os, shared_ptr<Derived> value) { return os << "x: " << value->x; }
};
int main () {
Base* a = new Base(1);
Derived* d = new Derived(6);
Base* b = new Derived(7);
shared_ptr<Base> ba = make_shared<Base>(3);
shared_ptr<Derived> de = make_shared<Derived>(4);
shared_ptr<Base> bd = make_shared<Derived>(5);
cout << "a is: " << a << endl;
cout << "*a is: " << *a << endl;
cout << "d is: " << d << endl;
cout << "*d is: " << *d << endl;
cout << "b is: " << b << endl;
cout << "*b is: " << *b << endl << endl;
cout << "ba is: " << ba << endl;
cout << "*ba is: " << *ba << endl;
cout << "de is: " << de << endl;
cout << "*de is: " << *de << endl;
cout << "bd is: " << bd << endl;
cout << "*bd is: " << *bd << endl;
delete a;
delete d;
delete b;
return 0;
}
live code
This spits out
a is: 0x1fe2bb0
*a is: i: 1
d is: 0x1fe2bd0
*d is: x: 6
b is: 0x1fe2bf0
*b is: i: 14
ba is: i: 3
*ba is: i: 3
de is: x: 4
*de is: x: 4
bd is: i: 10
*bd is: i: 10
But I would like to see *b print 7 and bd print 5 (ie. use the Derived class' insertion operator)
Ok, after searching a lot more, I found this answer. After smacking my head and saying 'of course', I summarize:
Don't overload the insertion operator since you can't make it virtual (it's not a member function), instead declare a virtual function in the base class that is overridden in the child classes. The insertion operator uses this function:
class Base {
...
friend inline ostream& operator<<(ostream& os, shared_ptr<Base> value) { return value->display(os, value); }
virtual ostream& display(ostream& os) { os << "i: " << i << endl; }
};
class Derived : public Base {
...
ostream& display(ostream& os) { os << "x: " << x << endl; }
};
live code
I have defined a normal class T:
class T {
public:
T() { cout << "default constructor " << this << "\n" }
T(const & T a) { cout <<"constructor by copy " << this << "\n"}
~T() { cout << "destructor "<< this << "\n"}
T & operator=(T & a) {
cout << " assignemnt operator :" << this << " = " << &a << endl;}
};
And there are 4 functions, one of which is wrong:
T f1(T a){ return a; }
T f2(T &a){ return a; }
T &f3(T a){ return a; }
T &f4(T &a){ return a; }
Does anyone know which one is wrong?
f3 is wrong, because it is returning a reference to a local object.
Parameters passed by value are copied. Their copies are local to functions to which they are passed - they go out of scope as soon as the function returns.