temporary objects get destroyed and corrupt program

I believe the following code has a bug related to temporary objects getting destroyed leaving dangling references... however I don't know how to find this in the code... could you please help me?

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
struct plus;
struct minus;

using namespace std::literals;

// expression tree node
template<class L, class OpTag, class R>
struct Expression
{
	Expression(L l, R r) : l{ l }, r{ r } {}
	~Expression() {}

	auto operator[](unsigned index) const
	{
		return OpTag::template apply(l[index], r[index]);
	}

	L const& l;
	R const& r;
};


// addition operator
template<class L, class R>
Expression<L, plus, R> operator+(L const& l, R const& r)
{
	return Expression<L, plus, R>(l, r);
}
template<unsigned Cols>
struct Vector
{
	float data[Cols];

	Vector() = default;
	Vector(const Vector& rhs) = default;
	Vector(std::initializer_list<float> list)
	{
		auto p = std::begin(list);
		for (int i = 0; i < Cols; ++i, ++p) { data[i] = *p; }
	}
	~Vector() = default;

	float operator[](size_t index) const
	{
		return data[index];
	}

	float& operator[](size_t index)
	{
		return data[index];
	}

	Vector& operator=(const Vector<Cols>& rhs)
	{
		for (int i = 0; i < Cols; ++i)
		{
			data[i] = rhs[i];
		}
		return *this;
	}
};

template<size_t N>
Vector<N> operator+(const Vector<N>& lhs, const Vector<N>& rhs)
{
	Vector<N> res;
	for (int i = 0; i < N; ++i)
	{
		res[i] = lhs[i] + rhs[i];
	}
	return res;
}



template<unsigned Rows, unsigned Cols>
struct Array
{
	Vector<Cols> data[Rows];

	Array() {}
	Array(const std::initializer_list<Vector<Cols>>& list)
	{
		auto p = std::begin(list);
		for (int i = 0; i < Rows; ++i, ++p) { data[i] = *p; }
	}
	~Array() = default;
	Array(const Array& rhs) = default;

	template<typename Expr>
	Array& operator=(Expr const& x)
	{
		for (unsigned i = 0; i < Rows; ++i)
		{
			(*this)[i] = x[i];
		}
		return *this;
	}

	const Vector<Cols>& operator[](size_t index) const
	{
		return data[index];
	}
	Vector<Cols>& operator[](size_t index)
	{
		return data[index];
	}

};


struct plus
{
	template<size_t N>
	static auto apply(Vector<N> a, Vector<N> b)
	{
		return a + b;
	}
};
void useExpr()
{
	// Vector<2> v{ 1.3,2.3 };
	Array<2, 2> x;

	Vector<2> a0{ 1,2 };
	Vector<2> a1{ 3,4 };
	Array<2, 2> a{ a0, a1 };

	Vector<2> b0{ 2,5 };
	Vector<2> b1{ 5,8 };
	Array<2, 2 > b{ b0, b1 };

	Vector<2> c0{ 3,6 };
	Vector<2> c1{ 7,9 };
	Array<2, 2> c{ c0,c1 };

	x = a + b + c;
	for (int i = 0; i < 2; ++i)
		for (int j = 0; j < 2; ++j)
			std::cout << x[i][j];

}


The problem is that x should have a result of {6,13},{15,21} but it doesn't

upon debugging I noted that there were many temporary objects being destroyed corrupting the stack and the values of local variables

Please all I need is some guidance!

Regards,
Juan Dent
What is the output for you?
My output is:
6121418
(VS 2022)

Edit: Output when
1
2
L const& l;
R const& r;
is replaced with
1
2
L l;
R r;

is:
6131521

which is what you're expecting.

So at least that narrows down the issue a bit, but sorry I don't have an answer.

May be related: https://stackoverflow.com/a/25845843
It won't compile if you add
Expression(const Expression&&) = delete; // prevents rvalue binding
Last edited on
Thank you!!!!
I was taking a reference to local objects in Expression ctor. So your solution builds copies of the arguments or I can make arguments const references to L and R in the ctor, like so:

1
2
3
	Expression(L const& l, R const& r) : l{ l }, r{ r } {}
	L const& l;
	R const& r;


That's it!!


Solved!


Many thank yous....
Haha, glad you got it; I didn't make the connection until you pointed it out. It's a shame the compiler doesn't see this class of bug.
Last edited on
That's it!!
Not quite. Having references as member variables are always risky.

You can have this:
1
2
3
4
5
6
7
8
9
10
11
12
...
	Expression(L const& l, R const& r) : l{ l }, r{ r } {}
	L const& l;
	R const& r;

...

Expression<int, int> e{1, 2}; // This will generate dangling references

int l = 1;
int r = 2;
Expression<int, int> e{l, r}; // Ok 
The reason for the dangling references is that a temporary objects is created when no reference able object is provide but a suitable constructor exists.
Topic archived. No new replies allowed.