I'm calling std::transform with a lambda that takes by reference and gives back a reference to the vector element. However, according to my program output, the copy constructor is called and the objects are NOT the same.
Code:
#include <algorithm>
#include <iostream>
#include <vector>
class Math
{
private:
int val_ = 5;
public:
Math(const Math& m) {
std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
}
Math(int val) : val_(val) {
std::cout << "Object constructed with " << val << std::endl;
}
};
int main()
{
std::vector<Math> v_math = { { 5 }, { 10 } };
std::transform(
begin(v_math),
end(v_math),
begin(v_math),
[](const Math& m)-> const Math& {
return m;
});
}
Output (Godbolt):
Object constructed with 5
Object constructed with 10
Copy constructor, our address: 0x23d7ec0, his address: 0x7fff9dc499a8
Copy constructor, our address: 0x23d7ec4, his address: 0x7fff9dc499ac
So three things are unclear to me right now:
Why are the objects different? Shouldn't they be the same?
Why is one object's address bigger than the other? Is this because the copied-to object remains on the stack which has offset-pointers?
How can I avoid copy construction as well (actually I just "misuse" std::transform for a declarative way of invoking a lambda on every std::vector element)?
The copies have nothing to do with your usage of std::transform. They happen when you construct your v_math std::vector, because you're using a std::initializer_list constructor, which forces copies during construction.
In your std::transform call, operator=(const Math&) is called, change your code to the following to see this.
class Math
{
private:
int val_ = 5;
public:
Math(const Math& m) {
std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
}
Math(int val) : val_(val) {
std::cout << "Object constructed with " << val << std::endl;
}
Math& operator=(const Math& other) {
val_ = other.val_;
std::cout << "Operator=(const Math&) called!\n";
return *this;
}
};
int main()
{
std::vector<Math> v_math = { { 5 }, { 10 } };
std::cout << "After constructing v_math!\n";
std::transform(
begin(v_math),
end(v_math),
begin(v_math),
[](const Math& m)-> const Math& {
return m;
});
std::cout << "After std::transform call!\n";
}
Related
Some background
Today I saw, in the body of a function kaboom, a local shared_ptr pointing to a *global object and, being captured by reference and its pointee returned by reference by a lambda, i.e. [&local]() -> auto& { return *local; }; this lambda was stored somehow for further use after the function kaboom returned.
As soon as I saw this, I thought the code was invoking undefined behavior. I tried to make a minimal example to support my claim, and came up with the following.
#include <iostream>
#include <memory>
#include <functional>
#include <vector>
struct Resource {
const int i{3};
};
std::shared_ptr<Resource> global = std::make_unique<Resource>();
auto getSharedStuff() {
return global;
}
std::vector<std::function<Resource&()>> actions{};
void kaboom() {
std::shared_ptr<Resource> local = getSharedStuff();
actions.push_back([&local]() -> auto& { return *local; });
}
int main() {
kaboom();
std::cout << global->i << std::endl;
std::cout << actions[0]().i << std::endl;
}
The simple fact that the 2 couts give 3 and 0 respectively is proof for me that I'm observing UB (3 is correct, and 0 could have been anything, including 3, but I've been lucky that it was not 3, so the UB is well manifest).
Good.
The code I'm curious about
But before getting there, an intermediate version of the repro above was this:
#include <iostream>
#include <memory>
#include <functional>
#include <vector>
struct Resource {
Resource(Resource const&) = delete;
Resource(Resource&&) = delete;
Resource() { std::cout << this << "'s ctor" << std::endl; }
~Resource() { std::cout << this << "'s dtor" << std::endl; }
void operator()() { std::cout << this << "'s operator()" << std::endl; }
};
std::shared_ptr<Resource> global = std::make_unique<Resource>();
auto getSharedStuff() {
return global;
}
std::vector<std::function<Resource&()>> actions{};
void kaboom() {
std::shared_ptr<Resource> local = getSharedStuff();
actions.push_back([&local]() -> auto& { return *local; });
}
int main() {
kaboom();
std::cout << "---" << std::endl;
actions[0]()();
}
which can result in this output
0xc602b0's ctor
---
0x7f0f48f7f4a0's operator()
0xc602b0's dtor
I'm kind of ok with this, as actions[0]()() is dereferencing a destroyed shared_ptr and calling operator() on the screwed up result, so I accept that this can be screwup too.
What's funny, though, is that removing std::cout << "---" << std::endl; makes UB less manifest, as the output becomes something like this:
0xce92b0's ctor
0xce92b0's operator()
0xce92b0's dtor
So my question is: how can a "simple" line as std::cout << "---" << std::endl; affect this way the manifestation of UB?
I switched from c to c++ recently and just can't figure out what I'm doing wrong here.
I would like to access and set the member of a map via another function.
Here is my example which you can just copy to cpp.sh or so if you like
#include <iostream>
#include <map>
using namespace std;
struct test{
int i;
int j;
};
void addValues(test* val){
if (val == NULL){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
void printVal(test* val){
cout<<"finish " << val->i << " " << val->j;
}
int main()
{
map<string, test*> bla = {{"test1",NULL}};
addValues(bla.at("test1"));
printVal(bla.at("test1"));
return 0;
}
code from my project is a little bit more complex but it's basically this problem. I created a test in addValues() and have not deleted it. Why am I not able to print this value in printVal()? What am I missing?
Thanks in advance!
Parameters are passed by value. Pointers are no exception to that. Your addValues modifies a local copy of the pointer when a nullptr is passed. Modifying that local copy does not affect the pointer in the map. Pass the pointer by reference:
void addValues(test*& val){
if (val == nullptr){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
Or better yet, do not use raw pointers in the first place. Moreover, consider to write a constructor that initializes the members of test instead of relying on the caller to initialize them.
Example :
#include <iostream>
#include <map>
//using namespace std; NO teach yourself not to do this.
struct test
{
int i = 0; // <== in c++ you can initialize values of structs
int j = 0;
};
// this instead of printVal
std::ostream& operator<<(std::ostream& os, const test& t)
{
os << "i = " << t.i << ", j = " << t.j << "\n";
return os;
}
int main()
{
std::map<std::string, test> map =
{
{"test1",{1,1}},
{"test2",{2,2}},
};
// loop over all entries in the map
// range based for loop.
// each entry in the map is a key,value pair (not they key, not the value but a pair)
// https://en.cppreference.com/w/cpp/language/range-for
std::cout << "range based for over keyvalue pairs\n";
for (const auto& kv : map)
{
// note kv.second is where we use operator<< from earlier.
std::cout << "Key : " << kv.first << ", value : " << kv.second << "\n";
}
std::cout << "\n";
// structured bindings make code more readable
// https://en.cppreference.com/w/cpp/language/structured_binding
std::cout << "range based for using structured bindings \n";
for (const auto& [key, value] : map)
{
std::cout << "Key : " << key << ", value : " << value <<"\n";
}
std::cout << "\n";
return 0;
}
I defined a copy constructor to avoid having a shallow copy of the data member n, but it is not working, v2 is still getting changed when I change v1, what I am doing wrong?
#include<iostream>
using namespace std;
class Vector
{
public:
int *n;
Vector();
Vector(const Vector& vector );
};
Vector::Vector()
{
cout<<"Constructor called"<<endl;
}
Vector::Vector(const Vector& vector )
{
n=new int;
n=vector.n;
cout<<"Copy constructor called"<<endl;
}
int main()
{
Vector v1;
int x=5;
v1.n=&x;
Vector v2=v1;
cout <<"Vector v1 has n value: "<<*v1.n<<endl;
cout <<"Vector v2 has n value: "<<*v2.n<<endl;
*v1.n=499;
cout <<"Vector v1 has n value: "<<*v1.n<<endl;
cout <<"Vector v2 has n value: "<<*v2.n<<endl;
return 0;
}
The problem is that your copy constructor is implemented wrong. You try to fix the shallow copy issue, but your code includes a statement that re-introduces the same issue again.
On this line:
n=new int;
You correctly allocate a new int, which is fine.
But on this line:
n=vector.n;
You are throwing away the int you just allocated, and instead point n to the same int that the other vector's n points to. This is the shallow copy issue all over again.
To fix the issue properly, you need to copy the value of the other vector's n member into your newly allocated n member, eg:
n = new int;
*n = *(vector.n);
Or:
n = new int( *(vector.n) );
That being said, to fully comply with the "Rule of 3", you also need to add a destructor to free n (which means you can't make v1 point to a local int variable in main()), as well as add a copy assignment operator to copy the value of n (similar to the copy constructor). In C++11, you can optionally also comply with the "Rule of 5" by adding a move constructor and move assignment operator, as well.
Try this instead:
#include <iostream>
#include <utility>
//using namespace std; // <-- bad practice!
class Vector
{
private:
int *m_n;
public:
Vector(int x = 0);
Vector(const Vector &src);
Vector(Vector &&src);
~Vector();
Vector& operator=(const Vector &src);
Vector& operator=(Vector &&src);
// alternatively, both operators can be merged into one:
// Vector& operator=(Vector src);
// you should not give outside code directly access to the int* itself,
// that would promote bad practices, and potentially crash your class's
// internal code if outside code assigns a bad pointer.
//
// these accessors provide safer use of the *value* that the int* points
// at, let the class itself be the sole handler of the int* itself ...
int n() const;
void n(int x);
};
Vector::Vector(int x)
: m_n( new int(x) )
{
std::cout << "Converting Constructor called" << std::endl;
}
Vector::Vector(const Vector &src)
: m_n( new int(src.n()) )
{
std::cout << "Copy constructor called" << std::endl;
}
Vector::Vector(Vector &&src)
: m_n( src.m_n )
{
src.m_n = nullptr;
std::cout << "Move constructor called" << std::endl;
}
Vector::~Vector()
{
delete m_n;
std::cout << "Destructor called" << std::endl;
}
Vector& Vector::operator=(const Vector &src)
{
if (&src != this) {
*m_n = src.n();
}
std::cout << "Copy assignment called" << std::endl;
return *this;
}
Vector& Vector::operator=(Vector &&src)
{
Vector tmp(std::move(src));
std::swap(m_n, tmp.m_n);
std::cout << "Move assignment called" << std::endl;
return *this;
}
/* alternatively:
Vector& Vector::operator=(Vector src)
{
std::swap(m_n, src.m_n);
std::cout << "Assignment called" << std::endl;
return *this;
}
*/
int Vector::n() const
{
return *m_n;
}
void Vector::n(int x)
{
*m_n = x;
}
int main()
{
Vector v1(5);
Vector v2 = v1;
std::cout << "Vector v1 has n value: "<< v1.n() << std::endl;
std::cout << "Vector v2 has n value: "<< v2.n() << std::endl;
v1.n(499);
std::cout << "Vector v1 has n value: " << v1.n() << std::endl;
std::cout << "Vector v2 has n value: "<< v2.n() << std::endl;
return 0;
}
I have a templated class which wraps a std::vector called mObjects.
Class has an insert function which forwards parameters of the actually stored type in the MyArray instance (its called variadic template arguments I think). In this example, I store MyMesh type, but it can be any kind of type.
As you can see in the main() function, mObjects vector doesn't grow, its elements get overwritten over and over again.
Think about an object-pool kind of data structure.
Everything works as expected.
template <class T>
class MyArray
{
public:
MyArray(const int capacity) :
mObjects(capacity)
{
mNextIndex = 0;
}
template <typename... Args>
void insert(Args&&... args)
{
mObjects[mNextIndex] = T{ std::forward<Args>(args)... }; //PROBLEMATIC ASSIGNMENT
//... not relevant code
}
private:
int mNextIndex;
std::vector<T> mObjects;
};
int main()
{
MyArray<Mesh> sa(2);
sa.insert("foo",1111); //goes to mObjects[0]
sa.insert("bar",2222); //goes to mObjects[1], and tada, the vector is full
sa.remove(1111); //not implemented above, but not relevant. Remove func basically adjusts mNextIndex, so mObjects[0] will be overwritten upon next insert.
sa.insert("xxx",3333); //mObjects[0] gets overwritten from "foo" to "xxx" and 1111 to 3333
}
My problem is with one row above commented as //PROBLEMATIC ASSIGNMENT.
mObjects[mNextIndex] = T{ std::forward<Args>(args)... };
When that command executes 3 things happen:
MyMesh(const string s, int x) constructor is called, meaning an entire MyMesh gets allocated on stack here. Why? I just want to pass the forwarded arguments to an existing mObjects[mNextIndex] element.
operator=(MyMesh&& other) is called, and does assignment variable by variable between the temporary variable and mObjects[mNextIndex].
~cVMesh() is called meaning the temporary variable deallocates and dies.
I would like to get rid of #1 and #3. So don't want the "expensive" temporary object creation. I just wish to forward/assign the incoming MyMesh parameters to mObjects[mNextIndex]. Similarly like what std::vector.emplace_back() does, but to any location pointed by mNextIndex.
How can I forward only the parameters to an existing variable in C++, without instantiate temporary variables?
For completness, here is the MyMesh class which gets stored in the MyArray class. Nothing special just printing out some message, when constructor/destructor/assignement operator is called:
class Mesh
{
public:
Mesh()
{
cout << "Mesh()" << std::endl;
mStr = "";
mId = 99999999;
}
Mesh(const string s, int x)
{
cout << "Mesh(const string s, int x)" << std::endl;
mStr = s;
mId = x;
}
~Mesh()
{
cout << "~Mesh()" << std::endl;
}
Mesh& operator=(const Mesh& other)
{
cout << "operator=(const Mesh& other)" << std::endl;
cout << mStr << " becomes " << other.mStr << endl;
cout << mId << " becomes " << other.mId << endl;
mStr = other.mStr;
mId = other.mId;
return *this;
}
Mesh& operator=(Mesh&& other) noexcept
{
cout << "operator=(Mesh&& other)" << std::endl;
cout << mStr << " becomes " << other.mStr << endl;
cout << mId << " becomes " << other.mId << endl;
mStr = other.mStr;
mId = other.mId;
return *this;
}
Mesh(const Mesh& other)
{
cout << "Mesh(const Mesh& other)" << std::endl;
mStr = other.mStr;
mId= other.mId;
}
Mesh(Mesh&& other) noexcept
{
cout << "Mesh(Mesh&& other)" << std::endl;
mStr = other.mStr;
mId = other.mId;
other.mStr = "";
other.mId = 99999999;
}
string mStr;
int mId;
};
I think what you want is to reconstruct an arbitary element in the vector with new values
#include<vector>
template<class T, class... Args>
void create_at_nth_place(std::vector<T>& v, int n, Args&&...args){
auto& elem = v[n];
elem.~T();
new(&elem) T(std::forward<Args>(args)...);
}
struct S {
S();
S(int, bool);
template<class... Args>
S(Args&&...);
S(S&&) noexcept;
~S();
};
void f() {
std::vector<S> v(3);
create_at_nth_place(v, 2, 4323, false);
char a = 'a';
create_at_nth_place(v, 2, 'a', 123, 1232, 32.f, a);
}
Link: https://godbolt.org/g/3K9akZ
mObjects[mNextIndex] = T{ std::forward<Args>(args)... }; line creates a temporary object, performs a move(copy) assignment to object already stored in vector at specified position and finally destroys a temporary.
The whole MyArray class is rather useless since vector already has similar functionality.
vector<Mesh> sa;
sa.reserve(2);
sa.emplace_back("foo",1111); // Mesh constructor called once
sa.emplace_back("bar",2222); // Mesh constructor called once again
You might add:
void assign(const string& s, int x)
{
cout << "assign(const string s, int x)" << std::endl;
mStr = s;
mId = x;
}
And use it:
mObjects[mNextIndex].assign(std::forward<Args>(args)...);
If your mesh class is very heavy, you should consider having an array of pointers, this would eliminate spurious copies altogether. Wouldn't MyArray<std::shared_ptr<MyMesh>> work as is?
When I run the following program, the destructor is called twice and I'm trying to understand why?
#include <iostream>
#include <vector>
#include <algorithm>
class sample
{
public:
sample() { std::cout << "Constructor " << std::endl; }
~sample() { std::cout << "Destructor" << std::endl; }
void operator()(int i)
{
std::cout << i << " , " << std::endl;
}
};
int main()
{
std::vector<int> iNumbers;
for ( int i = 0 ; i < 5 ; ++i)
iNumbers.push_back(i);
std::for_each(iNumbers.begin() , iNumbers.end() , sample() );
}
The output is as folllows
Constructor
0 ,
1 ,
2 ,
3 ,
4 ,
Destructor
Destructor
Classic rule of three violation. Try this:
#include <iostream>
#include <vector>
#include <algorithm>
class sample
{
public:
sample() { std::cout << "Constructor " << std::endl; }
sample(const sample&) { std::cout << "Constructor (copy)" << std::endl; }
~sample() { std::cout << "Destructor" << std::endl; }
sample& operator=(const sample&) { return *this; }
void operator()(int i)
{
std::cout << i << " , " << std::endl;
}
};
int main()
{
std::vector<int> iNumbers;
for ( int i = 0 ; i < 5 ; ++i)
iNumbers.push_back(i);
std::for_each(iNumbers.begin() , iNumbers.end() , sample() );
}
Output is:
Constructor
0 ,
1 ,
2 ,
3 ,
4 ,
Constructor (copy)
Destructor
Destructor
The reason is that std::for_each takes its argument by value, and this results in a copy of the argument you provide being made.
Therefore, the destruction of the temporary you create by doing sample() will be responsible for one of those two destruction messages (the last one, since the temporary is destructed after the evaluation of the full expression that creates it).
The first destruction message, on the other hand, comes from the destruction of the copy that std::for_each is working on.
std::for_each will take the function object by value, causing it to be copied. So one destructor is called for the temporary object created by sample() and the other for the copy.
If you wrote a copy constructor, you would see that the functor is copied into the algorithm. Both copies are then destructed. There is a potential for the functor to be returned and there would be 3 copies, so one of the copies is being elided.