No constructor called?

When running this program line 55 in the code give these outputs in the terminal:
Copy c
Enough money ($150) for a goat!
Raised:50
Lost:150

Copy c - is for when purchase_goat copies payment in.
Raised:50 - is for when purchase_goat constructs the return Banknote
Lost:150 - is for when purchase_goat destructs payment.

What I don't understand is why there is no output for Banknote c construction. Is it not constructing c with an r-value (purchase_goat return statement)? Then should not the move constructor be called and "Move c" be outputed? Even if not the move constructor is called some constructor should be, but there are no outputs whatsoever (not raised, copy c, or move c). What am I missing?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <utility>

using namespace std;

class Banknote
{
public:
    Banknote(int v) : value{v} { cout << "Raised:" << value << endl ; }
    Banknote(Banknote& b) : value(b.value)
    {
        b.value = 0;
        cout << "Copy c" << endl;
    }
  Banknote(Banknote&& b) : value(b.value)
    {
        b.value = 0;
        cout << "Move c" << endl;
    }
  ~Banknote() { if ( value ) cout << "Lost:" << value << endl; }

  Banknote& operator=(Banknote& b)
    {
        std::swap(value, b.value);
        cout << "Move t" << endl;
        return *this;
    }
    operator int() const { return value; }
  
private:
  int value;
};

Banknote purchase_goat(Banknote payment)
{
  if ( payment > 100 )
  {
    cout << "Enough money ($" << payment << ") for a goat!" << endl;
    return Banknote{ payment - 100 };
  }
  else
  {
    cout << "Insufficient money ($" << payment << ") for a goat!" << endl;
    return payment;
  }
}

int main()
{
  Banknote a{150}; // Raised $150
  Banknote b{50};  // Raised $50

  {
    cout << "1" << endl;
    Banknote c{ purchase_goat(a) }; // $150 is enough, raises $50 in exchange to "c" and lost $150
    cout << "2" << endl;
    Banknote d{ purchase_goat(c) }; // $50 not enough, $50 exchange transferred to "d"
    cout << "3" << endl;
  } // lost $50 from "d"
  
  Banknote e{ purchase_goat(a) }; // no value left in "a"
  cout << "4" << endl;
  Banknote f{ purchase_goat(b) }; // $50 not enough, $50 exchange transferred to "e"
  cout << "5" << endl;

  return 0; // lost $50 from "f"
}
Pretty sure its an optimization, but I am getting lost in tracing it.
Have your ctors print the address of this in hex, see if that shows that it did a silent move of one object to another without creating an extra?
Last edited on
It's a weird "copy" constructor that clears the state of the original object:
1
2
3
4
Banknote(Banknote& b) : value(b.value)
{
        b.value = 0;
}
Last edited on
But that "raised" is for when purchase_goat constructs the return Banknote(line 39). I am looking for the output for banknote c. But there is no "raised", "copy c" or "move c" so how is it being constructed?
I think so. But I wasn't able to prove it in the time I had.
So when I write:
1
2
Banknote ret{payment -100};
return ret;


instead of:
 
return Banknote{ payment - 100 };


On line 39, I get this:
Raised:50
Move c
Lost:150

Which is what I wanted to get. Not exactly sure why that makes the difference though.
Last edited on
The important thing to understand here is:

If there is any visible difference in the "outcome" of your program (except for diagnostic messages) between the two variants above, then the code of your class is probably wrong and should be fixed!

If there is no difference in the "outcome" of your program, then you can just go with the variant where the compiler eliminated the unnecessary copy operation and be happy about the speed-up :-)
Last edited on
you do see that getting the expected output means it did extra work though? The other way is more efficient.
I see, thank you guys!
FWIW, using the following test code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Test
{
public:
	Test(const int v) : value(v) { }
	Test(const Test &other) : value(other.value) { std::cout << "A copy was made.\n"; ++copies; }
	int get_value(void) const { return value; }
	static size_t get_copies(void) { return copies; }
private:
	const int value;
	static size_t copies;
};

size_t Test::copies = 0U;

Test foo(const int value)
{
	Test ret = Test(value);
	return ret;
}

int main() {
	std::cout << "Hello World!" << std::endl;
	Test obj = foo(42);
	std::cout << Test::get_copies() << std::endl;
	std::cout << "Goodbye." << std::endl;
}


...I get the following output with option /Od (optimizations disabled):
1
2
3
4
Hello World!
A copy was made.
1
Goodbye.


...whereas with option /O2 (full optimizations) I get:
1
2
3
Hello World!
0
Goodbye.


(Tested with MSVC 2022)
Last edited on
Topic archived. No new replies allowed.