I was witnessing some unexpected behavior in a C++ application I am writing in Linux Ubuntu. I would construct an object with parameters and then put a copy of that object into a std::map using the assignment operator. I wrote a simple program to demonstrate this situation...
#include <iostream>
#include <string>
#include <map>
using namespace std;
class Foo
{
public:
Foo(void) : _x(0)
{
cout << "Default" << endl;
}
Foo(int a) : _x(a)
{
cout << "Param" << endl;
}
Foo(Foo const &foo) :
_x(foo._x)
{
cout << "Copy" << endl;
}
Foo& operator=(Foo const &foo)
{
cout << "Assignment" << endl;
if (this != &foo)
{
_x = foo._x;
}
return *this;
}
int get(void)
{
return _x;
}
private:
int _x;
};
int main(int argc, char *argv [])
{
std::map<int, Foo> foos;
Foo a_foo(10);
foos[100] = a_foo;
return 0;
}
Here I am just printing out which constructor/operator gets called in what order so I can see how the construction and assignment works in the "main" function.
When I run this in Windows I get my expected output...
Param
Default
Assignment
When I run this in Linux I get the following output...
Param
Default
Copy
Copy
Assignment
Why are the two extra copy constructors there? It seems very inefficient to create the object so many times?
Thanks!
The answer lies in stl_map.h. Its behaviour depends on whether you compile with C++11 support or not. If you do then the STL can take advantage of move semantics to avoid unnecessary copying. VC++ uses the new language features by default but if you use g++ or clang you need to get used to using the -std=c++0x flag in 4.2 or -std=c++11 in newer versions.
With -std=c++11 set the output with g++4.8 is:
Param
Default
Assignment
Edit: Thank you very much for clarifying for me that my assumption that this was down to move semantics was incorrect. I'm leaving this answer in place to direct users to this better one.
Related
so I thought adding unique to vector shouldn't work.
Why does it work for the below code?
Is it cause by not setting copy ctor as "deleted"??
#include <iostream>
#include <vector>
#include <memory>
class Test
{
public:
int i = 5;
};
int main()
{
std::vector<std::unique_ptr<Test>> tests;
tests.push_back(std::make_unique<Test>());
for (auto &test : tests)
{
std::cout << test->i << std::endl;
}
for (auto &test : tests)
{
std::cout << test->i << std::endl;
}
}
There is no copy here, only moves.
In this context, make_unique will produce an instance of unique pointer which is not named, and this push_back sees it as a r-value reference, which it can use as it wants.
It produce pretty much the same result than this code would:
std::vector<std::unique_ptr<Test>> tests;
auto ptr = std::make_unique<Test>();
tests.push_back(std::move(ptr));
This is called move semantics if you want to search more info on the matter. (and this only works from c++11 and beyond)
There are two overloads of std::vector::push_back according to https://en.cppreference.com/w/cpp/container/vector/push_back
In your case you will use the one with rvalue-ref so no copying required.
I have the following code:
#include <iostream>
using namespace std;
class A
{
public:
A ()
{
cout<<"cons"<<endl;
}
~A ()
{
cout<<"dest"<<endl;
}
};
A
gokul (void)
{
A p;
return p;
}
int
main ()
{
A a = gokul();
cout<<"done\n";
}
When I run it. I get the following output:
cons
done
dest
I was expecting the output to be:
cons --> p created,
cons --> for a, gokul returning
dest --> p destroyed, gokul returned
done
dest --> a destroyed, main returned
as the local variable "p" will be destroyed when the function gokul returns and the new variable "a" will be created, no? I have compiled with all three standards 03, 11 & 14 and I get the same result.
~
Your code failed to trace the copy constructor. You need to do this to get a better picture of when an object is created.
#include <iostream>
using namespace std;
class A
{
public:
A() { cout<<"cons"<<endl; }
~A() { cout<<"dest"<<endl; }
A(const &A) { cout << "copy constructed" << endl;}
};
A gokul (void)
{
A p;
return p;
}
int main ()
{
A a = gokul();
cout<<"done\n";
}
When you run this code in Visual Studio 2015, no optimizations, the output is as follows:
cons
copy constructed
dest
done
dest
When you change to release mode, the output is now this:
cons
done
dest
The reason for the second output to not have copy construction is due to the Named Return Value Optimization that is done to remove the copy.
However the point is that you really can't predict how many times a copy constructor is called. As you can see, the unoptimized version also works correctly.
If you have optimization turned on then you get this output because of compiler optimization Copy Elision enter link description here
Or if you run your program in debug mode (without optimization), you don't simply trace copy constructor that is used as #PaulMcKenzie explained.
You can try compile without optimization: /Od (in visual studio), -O0 in gcc. Or just run in debug mode. And then back to optimization version by /O2 or -O2.
Beware that from C++11 compiler can use move constructor if you swith to non optimized version /0d and still you don't get output. So you need to trace move constructor
A(const A&& o){
cout << "move ctor" << endl;
}
when I compile the following code:
#include <cstdio>
char retChar(){
return 'c';
}
int main(){
retChar() = 'f';
}
I got the following errors:
modify.cpp: In function ‘int main()’:
modify.cpp:8:14: error: lvalue required as left operand of assignment
I have searched similar questions to this error, but the answers just seem to be situation specific. And I didn't find the answer to my question yet.(maybe somewhere else?)
=======================================add=======================================
but in the situation below:
#include <iostream>
class int_Bag{
int a;
int b;
public:
int_Bag(const int x, const int y);
int_Bag(const int_Bag&c);
int_Bag& operator =(const int_Bag &c){
std::cout<< "copy assignment constructor called\n";
a = c.a;
b = c.b;
return *this;
}
~int_Bag();
void debug();
};
int_Bag::int_Bag(const int x, const int y):a(x),b(y){}
int_Bag::~int_Bag(){
std::cout<< "destructor called\n";
}
int_Bag::int_Bag(const int_Bag&c):a(c.a),b(c.b){
std::cout<< "copy constructor called\n";
}
int_Bag getBag(const int_Bag &c){
return c;
}
void int_Bag::debug(){
std::cout << a <<"," << b << std::endl;
}
int main(){
int_Bag *bag1 = new int_Bag(3,4);
int_Bag *bag2 = new int_Bag(5,6);
std::cout<< "assignment expression begins\n";
getBag(*bag1) = *bag2;
bag1->debug();
delete bag1;
delete bag2;
}
this program just compile and run well:
assignment expression begins
copy constructor called
copy assignment constructor called
destructor called
3,4
destructor called
destructor called
We can see from the results that, when getBag() is being executing, a temporary object is created by calling copy constructor. So I think for the first situation, that assignment doesn't work is not simply due to the fact that the returned char is temporary.
Why assignment getBag(*bag1) = *bag2;works here?
What is the difference between these two situations for assignment expression?
I suppose that, for the second situation, the whole assignment expression is divided into two steps: first to call getBag() then second the copy assignment is called to copy *bag2 to the newly created temporary object returned by getBag(). However, in first situation, retChar() returns a rvalue(also temporary) which can only appear on the RHS of an assignment as #Jonathan Leffler said.
The first situation stands alone just seems to be stupid question maybe(I see those negative remarks). Those questions I just added are actually what I am really confused about.
Looking forward to receiving comments on my supposal and other anwsers.
(from a c++ beginner ^^)
You are attempting to modify a temporary (an rvalue). That is not possible.
You can accomplish your goal in a few ways, but the best one likely is this:
int main()
{
char c = retChar();
c = 'f';
// ...
return 0;
}
You refer to code that has getObject() = something. That code is likely written in some form that is similar to this:
char& getChar()
{
static char c = 'c';
return c;
}
int main()
{
getChar() = 'f';
// ...
return 0;
}
This returns a reference (not a temporary). When you assign it to 'f', the value of the c variable in getChar becomes 'f'.
Original question
You can't assign to the result of a function like that. The function returns an r-value, something that can only appear on the RHS of an assignment. Contrast with an l-value which is something that can be assigned to.
If you really wanted to (you probably don't, though) you could write:
char *retChar(){
static char c = 'c';
return &c;
}
int main(){
*retChar() = 'f';
}
And now you've modified the variable inside the function retChar().
Amended question
This code does a more thorough job instrumenting your example:
#include <iostream>
class int_Bag
{
int a;
int b;
public:
int_Bag(const int x, const int y);
int_Bag(const int_Bag&c);
int_Bag& operator =(const int_Bag &c){
std::cout<< "copy assignment constructor called:\nold";
this->debug();
std::cout << "new";
c.debug();
a = c.a;
b = c.b;
return *this;
}
~int_Bag();
void debug() const;
};
int_Bag::int_Bag(const int x, const int y) : a(x), b(y)
{
std::cout << "basic(" << x << "," << y << ")\n";
}
int_Bag::~int_Bag()
{
std::cout << "destructor called";
this->debug();
}
int_Bag::int_Bag(const int_Bag &c) : a(c.a), b(c.b)
{
std::cout << "copy constructor called";
this->debug();
}
int_Bag getBag(const int_Bag &c)
{
std::cout << "getBag returns";
c.debug();
return c;
}
void int_Bag::debug() const
{
std::cout << "(" << a << "," << b << ")" << std::endl;
}
int main()
{
int_Bag *bag1 = new int_Bag(3, 4);
int_Bag *bag2 = new int_Bag(5, 6);
std::cout << "assignment expression begins\n";
getBag(*bag1) = *bag2;
std::cout << "assignment expression ended\n";
bag1->debug();
delete bag1;
delete bag2;
}
When I run it, the output is:
basic(3,4)
basic(5,6)
assignment expression begins
getBag returns(3,4)
copy constructor called(3,4)
copy assignment constructor called:
old(3,4)
new(5,6)
destructor called(5,6)
assignment expression ended
(3,4)
destructor called(3,4)
destructor called(5,6)
The value 5,6 is assigned to the temporary created/returned by getBag(), but is then destroyed. None of that affects the original 3,4 value in *bag1. The (anonymous) object created by getBag() is usable until the end of the full expression it appears in, but not beyond, so the whole assignment is still very dubious.
I was compiling with G++ 4.8.2 on Mac OS X 10.9.1 Mavericks, or G++ 4.8.1 on Ubuntu 13.10, in both cases with the options:
g++ -O3 -g -std=c++11 -Wall -Wextra -Werror intbag.cpp -o intbag
There was not the slightest murmur from the compilers, nor was valgrind 3.8.1 in the slightest perturbed by the memory access patterns.
I think C++ gives you enough rope to shoot yourself in the foot, or some such mixed metaphor.
You're trying to assign a character to a function, which isn't allowed!
I'm not sure why you think this might be allowed, or why you want to do it...
retChar() is a function. Functions are not like variables; they cannot be assigned to values. Your retChar() function returns a char that you can use in your main method. For example you could do:
char myChar = retChar();
and myChar would then be equal to f.
If you are finding yourself confused with these concepts then try having a look at this and this.
Let me demonstrate with a simple example:
class A
{
public:
A() { cout << "A::A()" << endl; }
A(A const& a) : _a(a._a) { cout << "A::(A Copy Const)" << endl; }
A(A& a) : _a(a._a) { cout << "A::(A Copy)" << endl; }
template <typename _T1>
A(_T1& v1) : _a(v1) { cout << "A::(T conversion)" << endl; }
~A() { cout << "A::~A()" << endl; }
void say() { cout << "A::say()" << endl; }
private:
int _a;
};
int main(int argc, char* argv[])
{
A a1(A(argc)); // Line 1: ERM?
a1.say();
return 0;
}
Couple of things:
Is there any harm in defining a const and non-const version of the copy constructor? Reason I've done this is that this apparently helps the compiler differentiate from the templated constructor, i.e.
A const a1(argc);
A a2(a1); // <-- correctly call the const copy ctor
A a3(argc);
A a4(a3); // <-- correctly call the non-const copy ctor
Is there a better way to ensure that in the above example, the copy constructor is always called over the templated constructor?
Secondly, from a pure coding perspective, Line 1 appears to be okay, the intention is to create a temporary A with argc, and then trigger the copy constructor, however I get the following exception (gcc 4.4.4):
error: request for member ‘say’ in ‘a1’, which is of non-class type ‘A(A)’
I believe that what's happening here is that the compiler thinks that a1 is a function definition, is this correct? If so, what is the correct way to write that particular line of code? The following appears to be a hack!
A a1(true ? A(argc) : A());
p.s. Please ignore all stylistic foobars and why exactly I want to do this...! :)
A templated constructor is never a (formal) copy constructor.
You're right that a declaration that could be a function declaration is treated as a function declaration. It's called "the most vexing parse" of C++. One workaround is to use extra parentheses, like T v(( U )).
It may be that adding an auto keyword would fix it also, I haven't tried. But since auto gains new meaning in C++0x, it's probably not a good idea to get into habit of using it even if it works for that problem in C++98.
Cheers & hth.
I've created a simple test case exhibiting a strange behavior I've noticed in a larger code base I'm working on. This test case is below. I'm relying on the STL Map's "[ ]" operator to create a pointer to a struct in a map of such structs. In the test case below, the line...
TestStruct *thisTestStruct = &testStructMap["test"];
...gets me the pointer (and creates a new entry in the map). The weird thing I've noticed is that this line not only causes a new entry in the map to be created (because of the "[ ]" operator), but for some reason it causes the struct's destructor to be called two extra times. I'm obviously missing something - any help is much appreciated!
Thanks!
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct TestStruct;
int main (int argc, char * const argv[]) {
map<string, TestStruct> testStructMap;
std::cout << "Marker One\n";
//why does this line cause "~TestStruct()" to be invoked twice?
TestStruct *thisTestStruct = &testStructMap["test"];
std::cout << "Marker Two\n";
return 0;
}
struct TestStruct{
TestStruct(){
std::cout << "TestStruct Constructor!\n";
}
~TestStruct(){
std::cout << "TestStruct Destructor!\n";
}
};
the code above outputs the following...
/*
Marker One
TestStruct Constructor! //makes sense
TestStruct Destructor! //<---why?
TestStruct Destructor! //<---god why?
Marker Two
TestStruct Destructor! //makes sense
*/
...but I don't understand what causes the first two invocations of TestStruct's destructor?
(I think the last destructor invocation makes sense because testStructMap is going out of scope.)
The functionality of std::map<>::operator[] is equivalent to
(*((std::map<>::insert(std::make_pair(x, T()))).first)).second
expression, as specified in the language specification. This, as you can see, involves default-constructing a temporary object of type T, copying it into a std::pair object, which is later copied (again) into the new element of the map (assuming it wasn't there already). Obviously, this will produce a few intermediate T objects. Destruction of these intermediate objects is what you observe in your experiment. You miss their construction, since you don't generate any feedback from copy-constructor of your class.
The exact number of intermediate objects might depend on compiler optimization capabilities, so the results may vary.
You have some unseen copies being made:
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct TestStruct;
int main (int argc, char * const argv[]) {
map<string, TestStruct> testStructMap;
std::cout << "Marker One\n";
//why does this line cause "~TestStruct()" to be invoked twice?
TestStruct *thisTestStruct = &testStructMap["test"];
std::cout << "Marker Two\n";
return 0;
}
struct TestStruct{
TestStruct(){
std::cout << "TestStruct Constructor!\n";
}
TestStruct( TestStruct const& other) {
std::cout << "TestStruct copy Constructor!\n";
}
TestStruct& operator=( TestStruct const& rhs) {
std::cout << "TestStruct copy assignment!\n";
}
~TestStruct(){
std::cout << "TestStruct Destructor!\n";
}
};
Results in:
Marker One
TestStruct Constructor!
TestStruct copy Constructor!
TestStruct copy Constructor!
TestStruct Destructor!
TestStruct Destructor!
Marker Two
TestStruct Destructor!
add the following to TestStruct's interface:
TestStruct(const TestStruct& other) {
std::cout << "TestStruct Copy Constructor!\n";
}
Your two mysterious destructor calls are probably paired with copy constructor calls going on somewhere within the std::map. For example, it's conceivable that operator[] default-constructs a temporary TestStruct object, and then copy-constructs it into the proper location in the map. The reason that there are two destructor calls (and thus probably two copy constructor calls) is implementation-specific, and will depend on your compiler and standard library implementation.
operator[] inserts to the map if there is not already an element there.
What you are missing is output for the compiler-supplied copy constructor in your TestStruct, which is used during container housekeeping. Add that output, and it should all make more sense.
EDIT: Andrey's answer prompted me to take a look at the source in Microsoft VC++ 10's <map>, which is something you could also do to follow this through in all its gory detail. You can see the insert() call to which he refers.
mapped_type& operator[](const key_type& _Keyval)
{ // find element matching _Keyval or insert with default mapped
iterator _Where = this->lower_bound(_Keyval);
if (_Where == this->end()
|| this->comp(_Keyval, this->_Key(_Where._Mynode())))
_Where = this->insert(_Where,
value_type(_Keyval, mapped_type()));
return ((*_Where).second);
}
so the lesson is - dont put structs in a map if you care about their lifecycles. Use pointers, or even better shared_ptrs to them
You can check it out through this more simple code.
#include <iostream>
#include <map>
using namespace std;
class AA
{
public:
AA() { cout << "default const" << endl; }
AA(int a):x(a) { cout << "user const" << endl; }
AA(const AA& a) { cout << "default copy const" << endl; }
~AA() { cout << "dest" << endl; }
private:
int x;
};
int main ()
{
AA o1(1);
std::map<char,AA> mymap;
mymap['x']=o1; // (1)
return 0;
}
The below result shows that (1) line code above makes (1 default const) and (2 default copy const) calls.
user const
default const // here
default copy const // here
default copy const // here
dest
dest
dest
dest