I'm new to programming and I have to Overload the += operator , + Operator and ++ post and prefix operator so the following operations work properly but I'm stuck.
Here is the number class:
#include <iostream>
class Number
{
private:
int m_value;
public:
Number(int value = 0);
std::ostream& print(std::ostream& ostr = std::cout)const;
};
std::ostream& operator<<(std::ostream& ostr, const Number& N);
using namespace std;
void prn(const Number& a, const Number& b, const Number& c)
{
cout << "c a b " << endl
<< c << " " << a << " " << b << endl;
cout << "--------------------" << endl;
}
int main()
{
Number a{ 10 }, b{ 20 }, c;
c = a + b;
prn(a, b, c);
c = ++a;
prn(a, b, c);
c = a += b;
prn(a, b, c);
c = b++;
prn(a, b, c);
return 0;
}
/// output:
/*
c a b
30 10 20
--------------------
c a b
11 11 20
--------------------
c a b
31 31 20
--------------------
c a b
20 31 21
----------------
The below program shows how to overload different operator correctly.
#include <iostream>
class Number
{
//overload operator+ so that we can add two Number objects
friend Number operator+(const Number &lhs, const Number &rhs);
//overload operator<< so that we can use std::cout<< c
friend std::ostream& operator<<(std::ostream &os, const Number& num);
int m_value = 0;
public:
Number(int value = 0);
//overload operator+= . This operator will be used inside operator+ definition
Number& operator+=(const Number &rhs);
Number& operator++();
Number operator++(int);
};
Number::Number(int value): m_value(value)
{
}
Number operator+(const Number &lhs, const Number &rhs)
{
Number sum = lhs; // copy data members from lhs into sum
sum += rhs; // add rhs into sum using compound operator
return sum;
}
Number& Number::operator+=(const Number &rhs)
{
m_value += rhs.m_value;
return *this;
}
std::ostream& operator<<(std::ostream &os, const Number& num)
{
os << num.m_value;
return os;
}
Number& Number::operator++()
{
++m_value;
return *this;
}
Number Number::operator++(int)
{
Number old = *this; // copy old value
operator++(); // prefix increment
return old; // return old value
}
void prn(const Number& a, const Number& b, const Number& c)
{
std::cout << "c a b " << std::endl
<< c << " " << a << " " << b << std::endl;
std::cout << "--------------------" << std::endl;
}
int main()
{
Number a{ 10 }, b{ 20 }, c;
c = a + b;
prn(a, b, c);
c = ++a;
prn(a, b, c);
c = a += b;
prn(a, b, c);
c = b++;
prn(a, b, c);
return 0;
}
Now you'll get your desired output as can be seen here.
c a b
30 10 20
--------------------
c a b
11 11 20
--------------------
c a b
31 31 20
--------------------
c a b
20 31 21
--------------------
Related
Given the following code:
#include <iostream>
using namespace std;
class B {
private:
int n;
public:
B(int x) :
n(x) {
}
B operator+(B& b) {
return B(n + b.n);
}
friend ostream& operator<<(ostream &out, const B& b) {
out << "B: " << b.n;
return out;
}
bool operator<(const B& rhs) const {
return n < rhs.n;
}
};
int main() {
B b1(1), b2(2), b3(3);
const B b4 = b1 + (b2 + b3); // error
cout << b4 << b1 << b2 << b3;
return 0;
}
I know that if I will change this :
B operator+(B& b)
To:
B operator+(const B& b)
So it's will be ok. But, why it's really fix it?
invalid initialization of non-const reference of type 'B&' from an rvalue of type 'B'
(I understand the meaning of the error, but, I don't understand why the changing fix the error).
In addition, what is the difference between the code above (that give me error), to the following code:
class D: public B {
double m;
public:
D() :
B(0), m(0) {
}
D(int x, int y) :
B(x), m(y) {
}
D operator+(D& d) { // ~~
return d;
}
friend ostream& operator<<(ostream &out, const D& d) {
out << static_cast<const B&>(d);
out << ", D: " << d.m;
return out;
}
};
int main() {
B b4(4);
D d1(1, 0), d2(1, 2);
D d3 = d1 + d2;
cout << b4 << ", " << d3 << endl;
return 0;
}
Why now I don't get at ~~ also error?
This question already has answers here:
What are the basic rules and idioms for operator overloading?
(8 answers)
Closed 5 years ago.
can i add three objects using operator overloading in c++??
#include <iostream>
#include <conio.h>
using namespace std;
class complex{
private:
int a;
public:
void setdata(int x){
a=x;
}
void showdata(){
cout<<"\n"<<a;
}
int operator +(complex c,complex d){
complex temp;
temp.a=a+c.a+c+d.a;
return (temp);
}
};
int main(){
complex c1,c2,c3,c4;
c1.setdata(3);
c2.setdata(5);
c4.setdata(2);
c4=c1+c2+c3;
c4.showdata();
}
i am using this approach but it is not working please help.
you have to change a little the operator and you have a mistake in the inizialization of the variables (C3 is not initialized).
You have not to define an operator that works on three terms at the same time. The sum will be split in two parts (c1 + c2) + c3; the first sum returns a 'complex' item that is added to c3. The result of this last sum is assigned to c4.
See below the code.
#include <iostream>
#include <conio.h>
using namespace std;
class complex{
private:
int a;
public:
void setdata(int x){
a = x;
}
void showdata(){
cout << "\n" << a;
}
complex operator +(complex c){
complex temp;
int i = 0;
i = a + c.a;
temp.setdata(i);
return temp;
}
};
int main(){
complex c1, c2, c3, c4;
c1.setdata(3);
c2.setdata(5);
c3.setdata(2);
c4 = c1 + c2 + c3;
c4.showdata();
}
In my comments, I suggested two alternative solutions but while fiddling around with the sample code I even found a third one.
The sample code:
#include <iostream>
// Version 1: (return type fixed)
class Complex1 {
friend std::ostream& operator << (std::ostream &out, const Complex1 &c);
private:
int a;
public:
explicit Complex1(int a): a(a) { }
// operator + as member
Complex1 operator + (const Complex1 &c) const
{
return Complex1(a + c.a);
}
};
std::ostream& operator << (std::ostream &out, const Complex1 &c)
{
return out << c.a;
}
// Version 2: (two overloaded operators)
class Complex2 {
friend std::ostream& operator << (std::ostream &out, const Complex2 &c);
friend int operator+(const Complex2 &c, const Complex2 &d);
friend int operator+(int c, const Complex2 &d);
private:
int a;
public:
explicit Complex2(int a): a(a) { }
};
std::ostream& operator << (std::ostream &out, const Complex2 &c)
{
return out << c.a;
}
int operator+(const Complex2 &c, const Complex2 &d)
{
return c.a + d.a;
}
int operator+(int c, const Complex2 &d)
{
return c + d.a;
}
// Version 3: (implicit conversion with constructor)
class Complex3 {
friend std::ostream& operator << (std::ostream &out, const Complex3 &c);
private:
int a;
public:
Complex3(int a): a(a) { }
// operator + as member
int operator+(const Complex3 &c) const
{
return a + c.a;
}
};
std::ostream& operator << (std::ostream &out, const Complex3 &c)
{
return out << c.a;
}
// Check everything out:
using namespace std;
int main()
{
cout << "Version 1:" << endl;
{ Complex1 c1(3), c2(5), c3(2);
Complex1 c4 = c1 + c2 + c3;
cout << "c4: " << c4 << endl;
}
cout << "Version 2:" << endl;
{ Complex2 c1(3), c2(5), c3(2);
Complex2 c4 = Complex2(c1 + c2 + c3);
cout << "c4: " << c4 << endl;
}
cout << "Version 3:" << endl;
{ Complex1 c1(3), c2(5), c3(2);
Complex1 c4 = c1 + c2 + c3;
cout << "c4: " << c4 << endl;
}
cout << "done." << endl;
return 0;
}
You may compile and run the code on ideone.
Complex1 provides a fixed operator as member
Complex1 Complex1::operator + (const Complex1 &c) const;
Hence, (c1 + c2) + c3 is
(Complex1 × Complex1 → Complex1) × Complex1 → Complex1
Complex2 provides two overloaded operators as non-members
int operator+(const Complex2 &c, const Complex2 &d);
int operator+(int c, const Complex2 &d);
Hence, c1 + c2 is
Complex2 × Complex2 → int
and (c1 + c2) + c3 is
int × Complex2 → int
Complex3 is very similar like the original sample with the essential difference that I provided a non-explicit constructor which accepts an int. This means the compiler will use it as conversion operator when necessary.
(With a proper constructor, the operator issue probably hadn't been noticed soon.)
Thus, c1 + c2 is
Complex3 × Complex3 → int
and (c1 + c2) + c3 is
(int → Complex3) × Complex3 → int
I'm attempting to overload a += operator with the following line:
a = b += c += 100.01;
but my second += in that code shows an error as it matches no implementation i guess?
Heres my full relevant code:
main.cpp:
#include <iostream>
#include "Account.h"
using namespace sict;
void displayABC(const Account& a, const Account& b, const Account& c)
{
std::cout << "A: " << a << std::endl << "B: " << b << std::endl
<< "C: " << c << std::endl << "--------" << std::endl;
}
int main()
{
Account a("No Name");
Account b("Saving", 10000.99);
Account c("Checking", 100.99);
displayABC(a, b, c);
a = b + c;
displayABC(a, b, c);
a = "Joint";
displayABC(a, b, c);
a = b += c;
displayABC(a, b, c);
a = b += c += 100.01;
displayABC(a, b, c);
return 0;
}
Account.h (Relevant definitions)
class Account{
public:
friend Account operator+(const Account &p1, const Account &p2);
Account& operator+=(Account& s1);
Account & operator=(const char name[]);
friend double & operator+=(double & Q, Account & A);
Account & operator=(Account D);
};
Account operator+(const Account &p1, const Account &p2);
double operator+=(double& d, const Account& a);
};
Account.cpp (relevant implementations)
Account& Account::operator+=(Account &s1) {
double b = this->balance_ + s1.balance_;
this->balance_ = b;
return *this;
}
Account & Account::operator=(Account D) {
strcpy(name_, D.name_ );
this->balance_ = D.balance_;
return *this;
}
Account & Account::operator=(const char name[])
{
strcpy_s(name_, name);
return *this;
}
double & operator+=(double & Q, Account & A)
{
Q = Q + A.balance_;
return Q;
}
So my question is, how do i change my implementations correctly to run this function: a = b += c += 100.01;
Thank you.
In this statement
a = b += c += 100.01;
at first expression
c += 100.01
is evaluated. However the class does not have a corresponding overloaded operator.
On the other hand if the class has a conversion constructor that converts an object of type double to type Account nevertheless the corresponding operatpr += should be declared like
Account& operator+=(const Account& s1);
^^^^^
Also take into account that these declarations
friend double & operator+=(double & Q, Account & A);
and
double operator+=(double& d, const Account& a);
differ.
How could I use operator overloading to add two objects without making it a member of of any object? Is this something to do with insertion operator overloading?
so instead of this, to use something more generic i.e. to be used with any objects?:
sameObjectType operator + ( const sameObjectType &x, const sameObjectType &y ) {
sameObjectType z;
z = x+y;
return z;
}
// I want to do the same for subtraction operatoR
sameObjectType operator - ( const sameObjectType &x, const sameObjectType &y ) {
sameObjectType z;
z = x-y;
return z;
}
You can get the idea from this sample code.
#include <iostream>
class One {
private:
int num1, num2;
public:
One(int num1, int num2) : num1(num1), num2(num2) {}
One& operator += (const One&);
friend bool operator==(const One&, const One&);
friend std::ostream& operator<<(std::ostream&, const One&);
};
std::ostream&
operator<<(std::ostream& os, const One& rhs) {
return os << "(" << rhs.num1 << "#" << rhs.num2 << ")";
}
One& One::operator+=(const One& rhs) {
num1 += rhs.num1;
num2 += rhs.num2;
return *this;
}
One operator+(One lhs, const One &rhs)
{
return lhs+=rhs;
}
int main () {
One x(1,2), z(3,4);
std::cout << x << " + " << z << " => " << (x+z) << "\n";
}
I am trying to do operations between large objects and I experiment with r-value references to avoid temporary object creations.
The experiment is the following code, but the result is not what I expected.
The code:
#include <iostream>
using namespace std;
struct A
{
A() = default;
A(const A& a) { cout << "copy ctor" << endl; }
A(A&& a) { cout << "move ctor" << endl; }
A &operator=(const A& a) { cout << "copy assign" << endl; return *this; }
A &operator=(A&& a) { cout << "move assign" << endl; return *this; }
A &operator*=(double s) { cout << "this = this *= s" << endl; return *this; }
A operator*(double s) const { cout << "A = const this * s" << endl; return *this; }
A &operator+=(const A &b) { cout << "this = this + const A&" << endl; return *this; }
A operator+(const A &b) const { cout << "A = const this + const A&" << endl; return *this; }
A &operator+(A &&b) const { cout << "A&& = const this + A&& --> "; return b += *this; }
};
A &operator+(A &&a, const A &b) { cout << "A&& = A&& + const A& --> "; return a += b; }
A &operator*(A &&a, double s) { cout << "A&& = A&& * s --> "; return a *= s; }
int main()
{
A a,b,c,d;
a = b + a * 4 + /*operator*(static_cast<A&&>(d), 2)*/ d * 2 + (A() + c) * 5;
return 0;
}
The output:
A&& = A&& + const A& --> this = this + const A& // A() + c
A = const this * s // (...) * 5
copy ctor // ???
A = const this * s // d * 2
copy ctor // ???
A = const this * s // a * 4
copy ctor // ???
A&& = const this + A&& --> this = this + const A& // (d*2) + (...)
A&& = const this + A&& --> this = this + const A& // (a*4) + (...)
A&& = const this + A&& --> this = this + const A& // b + (...)
copy assign // a = (...)
What I expect:
A&& = A&& + const A& --> this = this + const A& // A() + c
A&& = A&& * s --> this = this *= s // (...) * 5
A&& = A&& * s --> this = this *= s // (...) * 2 d is not used anymore, so I want to move semantics
A = const this * s // a * 4 a is not used anymore, but I want to keep semantics
A&& = A&& + const A& --> this = this + const A& // (d*2) + (...)
A&& = A&& + const A& --> this = this + const A& // (a*4) + (...)
A&& = A&& + const A& --> this = this + const A& // b + (...)
move assign // a = (...)
Here's a more correct version with fewer copies:
#include <iostream>
#include <utility>
using namespace std;
struct A
{
A() = default;
A(const A& a) { cout << "copy ctor" << endl; }
A(A&& a) { cout << "move ctor" << endl; }
A &operator=(const A& a) { cout << "copy assign" << endl; return *this; }
A &operator=(A&& a) { cout << "move assign" << endl; return *this; }
A &operator*=(double s) { cout << "this *= s" << endl; return *this; }
A &operator+=(const A &b) { cout << "this += const A&" << endl; return *this; }
};
A&& operator+(A &&a, const A &b)
{ cout << "A&& + const A&" << endl; a+=b; return std::move(a); }
A&& operator+(A &&a, A &&b)
{ cout << "A&& + A&&" << endl; a+=b; return std::move(a); }
// I assume commutativity
A&& operator+(const A &a, A &&b)
{ cout << "const A& + A&&" << endl; b+=a; return std::move(b); }
A operator+(const A &a, const A &b)
{ cout << "const A& + const A&" << endl; A r(a); r+=b; return r; }
A&& operator*(A &&a, double s)
{ cout << "A&& * s" << endl; a*=s; return std::move(a); }
A operator*(const A& a, double s)
{ cout << "const A& * s" << endl; A r(a); r*=s; return r; }
int main()
{
A a,b,c,d;
a = b + a * 4 + d * 2 + (A() + c) * 5;
return 0;
}
and here's the (annotated) output with ts being temporaries created:
expression level actual operations
---------------- -----------------
const A& * s t1 = a * 4
copy ctor create t1 = copy a
this *= s t1 *= 4
const A& + A&& b + t1
this += const A& t1 += b
const A& * s t2 = d * 2
copy ctor create t2 = copy d
this *= s t2 *= 2
A&& + A&& t1 + t2
this += const A& t1 += t2
A&& + const A& A() + c (note: A() is already a temporary)
this += const A& A() += c
A&& * s A'() * 5
this *= s A'() *= 5
A&& + A&& t1 + A''()
this += const A& t1 += A''()
move assign a = t1 a = t1
I don't think you can expect it any better than just two temporaries for the whole expression.
Concerning your commented-out code: try std::move(d) instead of plain d and you will safe the copy of d in the above output and reduces the number of temporaries to one. If you also add std::move(a), the whole expression is evaluated without a single temporary!
Also note that without std::move(d) and std::move(a), the compiler has no clue that it should/could move those objects, so any code which ends up moving them anyways is dangerous and plain wrong.
Update: I turned my ideas into a library, find it at GitHub. With this, your code becomes as simple as:
#include <iostream>
using namespace std;
#include <df/operators.hpp>
struct A : df::commutative_addable< A >, df::multipliable< A, double >
{
A() = default;
A(const A& a) { cout << "copy ctor" << endl; }
A(A&& a) { cout << "move ctor" << endl; }
A &operator=(const A& a) { cout << "copy assign" << endl; return *this; }
A &operator=(A&& a) { cout << "move assign" << endl; return *this; }
A &operator*=(double s) { cout << "this *= s" << endl; return *this; }
A &operator+=(const A &b) { cout << "this += const A&" << endl; return *this; }
};
while still being efficient and avoiding any unneccesary temporaries. Enjoy!
First of all A() + c returns by lvalue reference. That makes the expression itself an lvalue.
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
An lvalue can't bind to an rvalue reference, so the member version of operator* is chosen. Your non-member functions should probably be returning by value:
A operator+(A &&a, const A &b) { cout << "A&& = A&& + const A& --> "; return a += b; }
A operator*(A &&a, double s) { cout << "A&& = A&& * s --> "; return a *= s; }
This causes the result to continue to be a prvalue expression referring to a temporary object.
Second, the copy constructor calls are caused by the member operators returning by value. This will cause a copy of the object. For example, when (...) * 5 returns, it will copy the value of *this out of the function:
A operator*(double s) const { cout << "A = const this * s" << endl; return *this; }
Your operators are implemented to return by value / by lvalue reference.
This results in chained operations accepting either object copy (hence the copy ctor) or lvalue reference.
E.g. b + a * 4 is equal to b.operator+(a.operator*(4)). The input to operator+ will be copy of the object.
Here is the signatures of your methods:
struct A
{
A() = default;
A(const A& a);
A(A&& a);
A &operator=(const A& a);
A &operator=(A&& a);
A &operator*=(double s);
A operator*(double s) const;
A &operator+=(const A &b);
A operator+(const A &b) const;
A &operator+(A &&b) const;
};
A &operator+(A &&a, const A &b);
A &operator*(A &&a, double s);
Problems show up right here. First, free operator+ should return an A&& that it is passed in, to avoid changing the rvalue reference into an lvalue. The same is true of A &A::operator+(A &&b) const; -- it should return an A&&.
Next, your free operators are chaining into the += operators. Here is a cute technique:
template<typename T>
A&&operator+(A &&a, T&&b){ return std::move(a+=std::forward<T>(b)); }
template<typename T>
A&&operator*(A &&a, T&&b){ return std::move(a*=std::forward<T>(b)); }
where we blinding forward our arguments to the += operation.
This can be made more robust, error-wise, with the auto return value technique:
template<typename T>
auto operator+(A &&a, T&&b)->declval(std::move(a+=std::forward<T>(b)))
{ return std::move(a+=std::forward<T>(b)); }
template<typename T>
auto operator*(A &&a, T&&b)->declval(std::move(a*=std::forward<T>(b)))
{ return std::move(a*=std::forward<T>(b)); }
which bumps errors up 1 step in the parsing stack using SFINAE. (Note that the && in T&& and A&& have completely different meanings -- T&&'s && is being used in a type deduction context, so T can bind to any reference type, while A&&'s && is not being used in a type deduction context, so it means A&& binds to an rvalue.).
What follows next is a far more heavily marked up version with some basic modifications for both correctness and efficiencies sake. I keep track of the history of each instance in the name field -- manipulations of this field are not "real", and its value represents the "calculation" required to create a given instance.
I assume move operations move this state.
#include <iostream>
#include <utility>
struct A;
A &operator+=(A& a, std::string op);
A&&operator+=(A&& a, std::string op);
struct recurse_nl {
int& count() {
static int v = 0;
return v;
}
recurse_nl(){if (++count()>1) std::cout << " --> "; else if (count()>2) std::cout << " --> [";}
~recurse_nl(){if (--count() == 0) std::cout <<"\n"; else if (count()>1) std::cout << "]"; }
};
struct A
{
std::string name;
A() = delete;
A(std::string n):name(n) { recurse_nl _; std::cout << "AUTO ctor{"<<name<<"}";};
A(const A& o):name(o.name+"_c&") { recurse_nl _; std::cout << "COPY ctor{"<<name<<"}(const&)"; }
A(A&& o):name(std::move(o.name)) { recurse_nl _; std::cout << "ctor{"<<name<<"}(&&)"; }
A(A& o):name(o.name+"_&") { recurse_nl _; std::cout << "COPY ctor{"<<name<<"}(&)"; }
A &operator=(const A& rhs) { recurse_nl _; std::cout << "COPY assign{"<<name<<"}={"<<rhs.name<<"}"; this->name = rhs.name; return *this; }
A &operator=(A&& rhs) { recurse_nl _; std::cout << "move assign{"<<name<<"}={"<<rhs.name<<"}"; this->name = std::move(rhs.name); return *this; }
A &operator*=(double d) { recurse_nl _; std::cout << "this{"<<name<<"} *= s{"<<d<<"}"; return (*this) += "(*#)"; }
A operator*(double d) const { recurse_nl _; std::cout << "A = const this{"<<name<<"} * s{"<<d<<"}"; A tmp(*this); return std::move(tmp*=d); }
A &operator+=(const A &rhs) { recurse_nl _; std::cout << "this{"<<name<<"} += const A&{"<<rhs.name<<"}"; return ((*this)+="(+=")+=rhs.name+")"; }
A operator+(const A &rhs) const { recurse_nl _; std::cout << "A = const this{"<<name<<"} + const A&{"<<rhs.name<<"}"; return std::move(A(*this)+="(+)"); }
A&& operator+(A &&rhs) const { recurse_nl _; std::cout << "A&& = const this{"<<name<<"} + A&&{"<<rhs.name<<"}"; return std::move(rhs += *this); }
~A() { recurse_nl _; std::cout << "dtor{"<<name<<"}"; }
};
A &operator+=(A& a, std::string op)
{ a.name+=op; return a; }
A&&operator+=(A&& a, std::string op)
{ a.name+=op; return std::move(a); }
template<typename T>
struct ref_type_of {
std::string value() const { return "value"; }
};
template<typename T>
struct ref_type_of<T&> {
std::string value() const { return "&"; }
};
template<typename T>
struct ref_type_of<T&&> {
std::string value() const { return "&&"; }
};
template<typename T>
struct ref_type_of<T const&&> {
std::string value() const { return " const&&"; }
};
template<typename T>
struct ref_type_of<T const&> {
std::string value() const { return " const&"; }
};
template<typename T>
std::string ref_type() { return ref_type_of<T>().value(); }
template<typename T>
A&& operator+(A &&a, T&& b) { recurse_nl _; std::cout << "A&&{"<<a.name<<"} = A&&{"<<a.name<<"} + T" << ref_type<T>(); return std::move(a += std::forward<T>(b)); }
template<typename T>
A&& operator*(A &&a, T&& b) { recurse_nl _; std::cout << "A&&{"<<a.name<<"} = A&&{"<<a.name<<"} * T" << ref_type<T>(); return std::move(a *= std::forward<T>(b)); }
void test1()
{
A a("a"),b("b"),c("c"),d("d");
a = b + a * 4 + d * 2 + (A("tmp") + c) * 5;
}
int main()
{
std::cout << "test1\n";
test1();
return 0;
}
I played with this on live work space and here is the output:
stdout:
test1
AUTO ctor{a}
AUTO ctor{b}
AUTO ctor{c}
AUTO ctor{d}
AUTO ctor{tmp}
A&&{tmp} = A&&{tmp} + T& --> this{tmp} += const A&{c}
A&&{tmp(+=c)} = A&&{tmp(+=c)} * Tvalue --> this{tmp(+=c)} *= s{5}
A = const this{d} * s{2} --> COPY ctor{d_c&}(const&) --> this{d_c&} *= s{2} --> ctor{d_c&(*#)}(&&) --> dtor{}
A = const this{a} * s{4} --> COPY ctor{a_c&}(const&) --> this{a_c&} *= s{4} --> ctor{a_c&(*#)}(&&) --> dtor{}
A&& = const this{b} + A&&{a_c&(*#)} --> this{a_c&(*#)} += const A&{b}
A&&{a_c&(*#)(+=b)} = A&&{a_c&(*#)(+=b)} + Tvalue --> this{a_c&(*#)(+=b)} += const A&{d_c&(*#)}
A&&{a_c&(*#)(+=b)(+=d_c&(*#))} = A&&{a_c&(*#)(+=b)(+=d_c&(*#))} + Tvalue --> this{a_c&(*#)(+=b)(+=d_c&(*#))} += const A&{tmp(+=c)(*#)}
move assign{a}={a_c&(*#)(+=b)(+=d_c&(*#))(+=tmp(+=c)(*#))}
dtor{a}
dtor{d_c&(*#)}
dtor{tmp(+=c)(*#)}
dtor{d}
dtor{c}
dtor{b}
dtor{a_c&(*#)(+=b)(+=d_c&(*#))(+=tmp(+=c)(*#))}
which is pretty verbose, but demonstrates pretty much every operation.
I modified your code so that operator+ and operator* actually creates a new object when required. The expensive operations (creating a new object, and copying) are highlighted through use of AUTO and COPY -- as you can see, there are the initial 4 alphabet objects, the tmp object in the expression, and two copies created by operator*(double).
We can get rid of some of the copies with this:
a = b + std::move(a) * 4 + std::move(d) * 2 + (A("tmp") + c) * 5;
however, we still end up with 3 objects with non-trivial state to destroy because twice we do operator+(A&&, A&&), and I didn't assume that this operation is extra efficient.
If it is, we can add this operator:
A &operator+=(A &&rhs) { recurse_nl _; std::cout << "this{"<<name<<"} += A&&{"<<rhs.name<<"}"; return ((*this)+="(+=")+=std::move(rhs.name)+")"; }
and the resulting output shows that only one object with non-trivial state is ever destroyed.
Final version on live workspace is here.
(The recurse_nl object is for recursion tracking. At the base level, it prints a newline at the end of the function. At deeper recursions, it does the --> printing, and in theory if recursion got deep enough it would print [ brackets to help).
Final output:
test1
AUTO ctor{a}
AUTO ctor{b}
AUTO ctor{c}
AUTO ctor{d}
AUTO ctor{tmp}
A&&{tmp} = A&&{tmp} + T& --> this{tmp} += const A&{c}
A&&{tmp(+=c)} = A&&{tmp(+=c)} * Tvalue --> this{tmp(+=c)} *= s{5}
A&&{d} = A&&{d} * Tvalue --> this{d} *= s{2}
A&&{a} = A&&{a} * Tvalue --> this{a} *= s{4}
A&& = const this{b} + A&&{a(*#)} --> this{a(*#)} += const A&{b}
A&&{a(*#)(+=b)} = A&&{a(*#)(+=b)} + Tvalue --> this{a(*#)(+=b)} += A&&{d(*#)}
A&&{a(*#)(+=b)(+=d(*#))} = A&&{a(*#)(+=b)(+=d(*#))} + Tvalue --> this{a(*#)(+=b)(+=d(*#))} += A&&{tmp(+=c)(*#)}
move assign{a(*#)(+=b)(+=d(*#))(+=tmp(+=c)(*#))}={a(*#)(+=b)(+=d(*#))(+=tmp(+=c)(*#))}
dtor{}
dtor{}
dtor{c}
dtor{b}
dtor{a(*#)(+=b)(+=d(*#))(+=tmp(+=c)(*#))}
where you can see the single "complex object" destroyed at the end (together with its entire history).