strange std::list iteration behavior - c++

#include <list>
#include <iostream>
#include <utility>
#include <conio.h>
std::list<int>&& asd()
{
std::list<int> a{ 4, 5, 6 };
return std::move( a );
}
int main()
{
std::list<int> a{ 1, 2, 3 };
std::list<int> &&b = std::move( a );
for( auto &iter : b )
{ std::cout<<iter<<' '; }
std::list<int> &&c{ asd() };
for( auto &iter : c )
{ std::cout<<iter<<' '; _getch(); }
}
this is what my program outputs:
1 2 3
4 3502376 454695192 3473720 4 3502376 454695192 3473720 4 3502376 454695192 3473720 4

std::list<int> a{ 4, 5, 6 };
This object is declared locally inside this function, so it gets destroyed when the function returns.
std::list<int>&& asd()
This function returns an rvalue reference. A reference to some unspecified object.
What happens here is that this "unspecified object" gets destroyed when this function returns, so the caller ends up holding a reference to a destroyed object.
return std::move( a );
The dirty little secret is that std::move() does not actually move anything. It never did, it never will. All that std::move() does is cast its parameter into an rvalue reference. Whether the object gets moved, or not, requires other dominoes to fall into place. std::move() merely provides one piece of the puzzle, but all other pieces of this puzzle must exist in order for an actual move to happen.
Here, those other pieces of the puzzle do not exist. Therefore, nothing gets moved. A reference to a destroyed object gets returned, and all subsequent use of the destroyed object results in undefined behavior.

Related

Passing functor object by reference doesn't work in std::generate_n()

I made a FibonacciSeries functor for using in std::generate_n(), but it doesn't work the way it should be. Passing the functor by reference doesn't work well.
In the following program, 0 1 1 2 3 5 8 13 is the expected output, but I get 0 1 1 2 0 1 1 2 as the output. What could be wrong?
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
inline void printvec(const vector<int>& vec)
{
for (const auto& v : vec)
cout << v << ' ';
cout << endl;
}
class FibonacciSeries
{
private:
int f1;
int f2;
int f3;
public:
FibonacciSeries() :f1(0), f2(1) {}
int operator() () {
f3 = f1 + f2;
int result = f1;
f1 = f2;
f2 = f3;
return result;
}
void printstat(){
cout << f1 << endl;
}
};
int main()
{
vector<int> vec;
FibonacciSeries series;
//passing function object by reference
generate_n<back_insert_iterator<vector<int>>,
int, FibonacciSeries&>(back_inserter(vec), 4, series);
printvec(vec);// prints 0 1 1 2
series.printstat();//prints 0//unexpected
//passing function object by value
generate_n(back_inserter(vec), 4, series);
printvec(vec);//prints 0 1 1 2 0 1 1 2//unexpected
}
I wasnt able to reproduce the issue with gcc trunk (https://godbolt.org/z/PfPPE67h8). However, standard algorithms may copy functors passed to them internally. Not sure what is actually going "wrong" in the implementation but suppose the algorithm starts with
std::remove_reference<Generator> g = generator;
And uses that to generate the output. One would have to read the implementation to see whats causing your output. However, using FibonacciSeries& as explicit template argument is the wrong approach. Even if the generator is passed by reference it may be copied internally.
The simplest to avoid such issues as you are observing now, is to not rely on functors with state. Rather than making the state member of the functor, let it capture state by reference and store the state elsewhere. Either use a lamba, as suggested by Sam in a comment, or write your custom wrapper:
struct FibonacciSeriesWrapper {
FibonacciSeries& parent;
int operator() () { return parent(); }
};
int main()
{
vector<int> vec;
FibonacciSeries series;
//passing function object by reference
generate_n(back_inserter(vec), 4, FibonacciSeriesWrapper{series});
generate_n(back_inserter(vec), 4, FibonacciSeriesWrapper{series});
printvec(vec);//prints 0 1 1 2 3 5 8 13
}
Live Demo
Your generator (FibonnachiSeries object) is copied to std::generate_n call. So original object state is not changed. One workaround may be passsing a lambda
generate_n(back_inserter(vec), 4,
[&series] { return series(); });
// UPD: nevermind, declaring FibonacchiSeries as a reference should have helped. Your version works fine for me. What compiler do you use?

Is storing initializer_lists undefined behaviour? [duplicate]

This question already has answers here:
C++: Life span of temporary arguments?
(4 answers)
Closed 7 months ago.
This question is a follow up to How come std::initializer_list is allowed to not specify size AND be stack allocated at the same time?
The short answer was that calling a function with brace-enclosed list foo({2, 3, 4, 5, 6}); conceptually creates a temporary array in the stackspace before the call and then passes the initializer list which (like string_view) just references this local temporary array (probably in registers):
int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});
Now consider the following case where I have nested initializer_lists of an object "ref". This ref object stores primitives types or an initializer_list recursively in a variant. My question now is: Is this undefined behaviour? It appears to work with my code, but does it hold up to the standard? My reason for doubting is that when the inner constructor calls for the nested brace-enclosed lists return, the temporary array which the initializer list is refering to could be invalidated because the stack pointer is reset (thus saving the initializer_list in the variant preserves an invalid object). Writing to subsequent memory would then overwrite values refered to by the initializer list. Am I wrong in believing that?
CompilerExplorer
#include <variant>
#include <string_view>
#include <type_traits>
#include <cstdio>
using val = std::variant<std::monostate, int, bool, std::string_view, std::initializer_list<struct ref>>;
struct ref
{
ref(bool);
ref(int);
ref(const char*);
ref(std::initializer_list<ref>);
val value_;
};
struct container
{
container(std::initializer_list<ref> init) {
printf("---------------------\n");
print_list(init);
}
void print_list(std::initializer_list<ref> list)
{
for (const ref& r : list) {
if (std::holds_alternative<std::monostate>(r.value_)) {
printf("int\n");
} else if (std::holds_alternative<int>(r.value_)) {
printf("int\n");
} else if (std::holds_alternative<bool>(r.value_)) {
printf("bool\n");
} else if (std::holds_alternative<std::string_view>(r.value_)) {
printf("string_view\n");
} else if (std::holds_alternative<std::initializer_list<ref>>(r.value_)) {
printf("initializer_list:\n");
print_list(std::get<std::initializer_list<ref>>(r.value_));
}
}
}
};
ref::ref(int init) : value_{init} { printf("%d stored\n", init); }
ref::ref(bool init) : value_{init} { printf("%s stored\n", init ? "true" : "false"); }
ref::ref(const char* init) : value_{std::string_view{init}} { printf("%s stored\n", init); }
ref::ref(std::initializer_list<ref> init) : value_{init} { printf("initializer_list stored\n", init); }
int main()
{
container some_container = { 1, true, 5, { {"itemA", 2}, {"itemB", true}}};
}
Output:
1 stored
true stored
5 stored
itemA stored
2 stored
initializer_list stored
itemB stored
true stored
initializer_list stored
initializer_list stored
---------------------
int
bool
int
initializer_list:
initializer_list:
string_view
int
initializer_list:
string_view
bool
First of all, copying a std::initializer_list does not copy the underlying objects. (cppreference)
and
container some_container = { 1, true, 5, { {"itemA", 2}, {"itemB", true}}};
actually compiles to something like
container some_container = { // initializer_list<ref>
ref{1},
ref{true},
rer{5},
ref{ // initializer_list<ref>
ref{ // initializer_list<ref>
ref{"itemA"},
ref{2}
},
ref{ // initializer_list<ref>
ref{"itemB"},
ref{true}
}
}
};
and all those object's lifetime* end at end of full expression (the ; here)
*including all initializer_list<ref>, their underlying array, and all the belonging ref objects, but not the one in the constructor (copy elision may apply though)
so
yes it's fine to use those object in the constructor.
those object are gone after the ; so you should not use the stored object (in initializer_list) anymore
godbolt example with noisy destructor and action after the construction

Captured shared_ptr released while executing the lambda body

I have a nested lambda in C++, which is to say, an inner lambda contained in a middle lambda, which is also contained in an outer lambda.
I created a shared_ptr in the outer lambda, which I passed by value to the middle lambda, inside which I created the inner lambda, after declaration of which the captured shared_ptr seems to be released, as can be confirmed with use_count() turning into zero. Why?
The full code:
#include <cstdio>
#include <functional>
#include <memory>
struct container;
using func_type = std::function<void(container&)>;
struct container {
explicit container(int id, func_type next) : id{id}, next{next} {
printf("contianer (%d)\n", id);
}
func_type next;
int id;
~container() {
printf("~contianer (%d)\n", id);
}
};
struct value_type {
~value_type() {
printf("~value_type\n");
}
};
int main() {
container c{0, [](container& c1) {
std::shared_ptr<value_type> value = std::make_shared<value_type>();
c1 = container{1, [value](container& c2) mutable {
printf("value.use_count(): %d\n", value.use_count());
c2 = container{2, [](container& c3) mutable {
printf("finished\n");
return;
}};
printf("value.use_count(): %d\n", value.use_count());
return;
}};
return;
}};
c.next(c);
c.next(c);
return 0;
}
Output: (godbolt: https://godbolt.org/z/9PbboEPfK)
Program returned: 0
Program stdout
contianer (0)
contianer (1)
~contianer (1)
value.use_count(): 1
contianer (2)
~value_type
~contianer (2)
value.use_count(): 0
~contianer (2)
When you call c.next(c) the first time, you are running the function c.next which will cause c.next to be replaced by a new lambda, the one that owns a shared pointer. After the first c.next(...) call this shared_ptr owning lambda will be the new c.next.
When you then call c.next(c) again you are replacing that lambda with one that has no ownership of the shared_ptr so the shared_ptr gets destructed as it should when the replacement assignment happens as nothing else has a reference to it except a closure that is going out of scope.
If this was real code -- which I mean if it's real code, do not do whatever you are doing this way -- you could get the behavior you seem to want by having the inner lambda, the one that just prints "finished", also capture value.

Is this a right behavior?

class foo {
std::vector<int> datas;
public:
template <typename It>
foo(It x, It y) {
for(; x != y; ++x)
insert(*x);
}
void insert(int val) {
if(val == 5)
throw 99;
else
datas.push_back(val);
}
void print() const {
for(int x : datas)
std::cout << x << " ";
}
};
int main() {
std::vector<int> v{1, 2, 3, 5, 6, 7};
foo* f;
try {
f = new foo(std::begin(v), std::end(v));
}catch(int const& x) {
}
f->print();
}
I suppose that f wouldn't be constructed in this case; the exception thrown by the insert method is not handled in the body of foo constructor.
I got the following output on gcc 8.2.0 :
1 2 3 5 6 7
I suppose that f wouldn't be constructed in this case;
A foo instance would not be constructed in this case.
Furthermore, f would not have been assigned to, so its value would remain indeterminate.
Is this a right behavior?
Accessing an object by indirecting through an indeterminate pointer has undefined behaviour.
While the undefined nature has been discussed, I feel like it might be interesting to look into actual produced result and speculate why we see it.
While I have no proof, I believe it is an interesting consequence of compiler optimizations and code reasoning. We see that the output is 1 2 3 5 6 7, even though the code has a throw statement for element 5. How would this happen?
In my view, since we have f->print() outside of try block, compiler reasons that f is never uninitialized - so constructor never throws. Since constructor never throws, val == 5 is never true! Because of that, the branch is removed. And thus we have the result we do have.

Deleting a reference to a variable without affecting the original

I have a struct which contains a reference to a vector.
I then create a vector of this struct.
I create three three instances of this struct in this vector.
I then erase the second instance of the struct in the vector.
This apparently causes the contents of testData2 to become the same as the contents of testData3!?
How do I stop this from happening? Is there something I should put in the destructor of the struct to stop this from happening?
Or is what I am doing here terribly bad and if so, why?
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
struct TESTSTRUCT
{
int &testInt;
std::vector<int> &testVector;
TESTSTRUCT(int &testIntInput, std::vector<int> &testVectorInput)
:testInt(testIntInput), testVector(testVectorInput) {}
TESTSTRUCT(const TESTSTRUCT &source)
:testInt(source.testInt), testVector(source.testVector) {}
TESTSTRUCT &operator=(const TESTSTRUCT &source)
{
testInt = source.testInt;
testVector = source.testVector;
return *this;
}
virtual ~TESTSTRUCT(){}
};
int main()
{
std::vector<int> testData1;
std::vector<int> testData2;
std::vector<int> testData3;
int a = 1;
int b = 2;
int c = 3;
testData1.push_back(10);
testData1.push_back(20);
testData1.push_back(30);
testData2.push_back(40);
testData2.push_back(50);
testData2.push_back(60);
testData3.push_back(70);
testData3.push_back(80);
testData3.push_back(90);
std::vector<TESTSTRUCT> *structVector = new std::vector<TESTSTRUCT>();
structVector->push_back(TESTSTRUCT(a, testData1));
structVector->push_back(TESTSTRUCT(b, testData2));
structVector->push_back(TESTSTRUCT(c, testData3));
std::cout<<&testData1[0]<<std::endl;
std::cout<<&testData2[0]<<std::endl;
std::cout<<&testData3[0]<<std::endl;
std::cout<<testData1[0]<<std::endl;
std::cout<<testData2[0]<<std::endl;
std::cout<<testData3[0]<<std::endl;
structVector->erase(structVector->begin()+1);
std::cout<<testData1[0]<<std::endl;
std::cout<<testData2[0]<<std::endl;
std::cout<<testData3[0]<<std::endl;
std::cout<<&testData1[0]<<std::endl;
std::cout<<&testData2[0]<<std::endl;
std::cout<<&testData3[0]<<std::endl;
return 0;
}
Output:
00711220
00711258
00717C68
10
40
70
10
70
70
00711220
00711258
00717C68
Press any key to continue . . .
EDIT:
This here is an example from a larger program. The instances of the struct are for things I want a renderer to draw, and those things can change their position (and hence where the renderer should draw them).
The issue is not immediately visible. Have a look at the equality operator of TESTSTRUCT :
TESTSTRUCT &operator=(const TESTSTRUCT &source)
{
testInt = source.testInt;
testVector = source.testVector;
return *this;
}
The testVector in this will obtain a copy of source.testVector.
Now, the structVector in main contains three instances of TESTSTRUCT, each bound to its own std::vector<int> via a reference. When you call structVector->erase(structVector->begin()+1);, TESTSTRUCT(b, testData2) is removed from the structVector.
The specification of erase says that all the vector's elements after the requested erasing position are shifted down (in terms of position) in order for the vector to be memory-contiguous. I don't know if the standard specifies anything about how erase needs to be implemented when it comes to copying, but in my implementation (g++), operator= is called. This means that the vector stored via reference inside the TESTSTRUCT instance referenced by the iterator structVector->begin()+1 (so, effectively, structVector[1]) gets the contents of structVector[2]. And because it's a reference, it's essentially the same thing as testData2 from main, which explains why its content is exactly the same as testData3.
This is valid, however, as you can see yourself, avoiding such code makes sense when debugging.
You are over using references (somewhat incorrectly). Your TESTSTRUCT objects contains references to testdata1, testdata2 and testdata3. So when you modify structVector, previous two vectors also get modified. So when you erase second element from structVector, the third element takes it's position. So the internal elements, i.e. the testdata2 is also replaced with testdata3in main, as their references are contained in the struct.