std::map: why copy constructor is called two times? - c++

Let's consider this simple code
#include <iostream>
#include <map>
class my_class {
std::string name;
int age;
public:
my_class():name(""), age(0) {
std::cout << "Default constructor" << std::endl;
}
my_class(std::string nm, int a) : name(nm), age(a) {
std::cout << "Constructor with both parameters" << std::endl;
}
my_class(const my_class& obj) {
std::cout << "copy_constructor " << std::endl;
name = obj.name;
age = obj.age;
}
void show() {
std::cout << "name=: " << name << "age=: " << age <<std::endl;
}
};
int main() {
std::map<int, my_class> m;
m[0];
return 0;
}
I compile it via this gcc (version of my gcc: gcc version 5.4.0 20160609):
g++ -o main main.cpp
After run we see that copy constructor has been called two times:
Default constructor
copy_constructor
copy_constructor
First call of copy constructor it is because of operator[]. A call to this function is equivalent to:
(*((this->insert(make_pair(k,mapped_type()))).first)).second
So we see that make_pair calls copy constructor of my_class.
But the reason of the second call of copy constructor is not clear for me. I'm only supposing that it creates a temporary object into make_pair implementation. Am I correct ?

Related

how to initialize a class object reference in C++ to simulate NRVO?

I meet a course programming problem, which asks me to initialize the A a using passing by reference (initialize the A a in the func). How can I call A's constructor by A's reference?
#include <iostream>
using namespace std;
class A
{
public:
int x;
A()
{
cout << "default constructor" << endl;
x = 1;
}
A(int x)
{
cout << "constructor with param = " << x << endl;
this->x = x;
}
~A() {
cout << "destructor" << endl;
}
void print() {
cout << x << endl;
}
};
void fun(A& a)
{
a.A::A(10); // error!
return;
}
int main()
{
A a;
fun(a);
a.print();
return EXIT_SUCCESS;
}
There is a background of this problem. The teacher want us to replicate the NRVO(named return value optimization) result.
#include <iostream>
using namespace std;
class A
{
public:
int x;
A()
{
cout << "default constructor" << endl;
x = 1;
}
A(int x)
{
cout << "constructor with param = " << x << endl;
this->x = x;
}
~A() {
cout << "destructor" << endl;
}
void print() {
cout << x << endl;
}
};
A fun() {
A a = A(10);
return a;
}
int main()
{
A a = fun();
return EXIT_SUCCESS;
}
default g++ compiler:
constructor with param = 10
destructor
if we close the NRVO:
g++ test.cpp -fno-elide-constructors
constructor with param = 10
destructor
destructor
destructor
destructor
The teacher want us to replicate the NRVO(named return value optimization) result by passing by reference.
The syntax a.A::A(10); is incorrect.
Constructor is used to create an object of a class, you cannot call it on an already existing object. Even a constructor cannot be explicitly called. It is implicitly called by the compiler.
From general-1.sentence-2:
Constructors do not have names.
Thus, you cannot call a constructor explicitly. The compiler will automatically call the constructor when an object of that class-type is created.
You can not, not like this.
A reference always points to an initialized object. So you already failed before you called the function. The "return" argument is already initialized. And you can't initialized an initialized value again, not legally.
You can cheat by calling
std::construct_at(&a, 10);
For it to really reflect NRVO you could have something like this:
void fun(A *a)
{
std::construct_at(a, 10);
}
union UninitializedA {
std::byte uninitialized[sizeof(A)];
A a;
};
int main()
{
UninitializedA u;
fun(&u.a);
u.a.print();
u.a.~A();
return EXIT_SUCCESS;
}

Constructors, destructors and ternary operator

I have this simple class with two specialized constructors.
#include <iostream>
class test_01
{
std::string name;
uint16_t value;
public:
test_01(std::string name);
test_01(uint16_t value);
~test_01();
void show_message(std::string message);
};
test_01::test_01(std::string name)
{
std::cout << "Constructor string is called" << std::endl;
test_01::name = name;
}
test_01::test_01(uint16_t value)
{
std::cout << "Constructor uint16 is called" << std::endl;
test_01::value = value;
}
test_01::~test_01()
{
std::cout << "Destructor is called" << std::endl;
}
void test_01::show_message(std::string message)
{
std::cout << message.c_str() << std::endl;
}
int main()
{
bool result = true;
test_01 t = result ? test_01("test") : test_01(57);
t.show_message("hello");
}
Each constructor is called depending on an external condition. When the ternary operator is executed a destructor is called. Thus.
Constructor string is called
Destructor is called
hello
Destructor is called
I don't understand why the first destructor is called
Thanks !
Because there are two test_01 objects created, so two must be destroyed.
first is exactly one of
vvvvvvvvvvvvvvv or vvvvvvvvvvvv
test_01 t = result ? test_01("test") : test_01(57);
^ second is here
Had you added logging to the copy or move constructors, you would see, a temporary object is copied/moved into t object, which is move/copy constructed from this temporary.

C++ default constructor is not used at runtime, but required at compile time

Here is my code. If I remove default constructor, there will be error below. But if I add a default constructor, it will have no issues compiling and running. Wondering why? I am especially confused since default constructor is not used at all in runtime, why it is required at compile time?
#include <iostream>
#include <vector>
#include <string>
class Foo {
public:
//Foo();
Foo(const std::string& name, double score);
Foo(const Foo& other);
Foo(Foo&& other);
const std::string& name() const { return name_; }
double score() const { return score_; }
private:
std::string name_;
double score_;
};
/*
Foo::Foo() {
std::cout << "In default constructor " << std::endl;
name_ = "foo";
score_ = 1.0;
}*/
Foo::Foo(const std::string& name, double score) : name_(name), score_(score) {
std::cout << "In parametered constructor " << std::endl;
}
Foo::Foo(const Foo& other) {
std::cout << "In copy constructor " << std::endl;
name_ = other.name();
score_ = other.score();
}
Foo::Foo(Foo&& other)
: name_(std::move(other.name())), score_(std::move(other.score())) {
std::cout << "In move constructor " << std::endl;
}
int main(int argc, char** argv) {
std::vector<Foo> students;
students.emplace_back("name1", 4.0);
students.emplace_back("name2", 5.0);
std::cout << "resizing begin " << std::endl;
students.resize(1);
for (Foo student : students) {
std::cout << "student name: " << student.name()
<< " student score: " << student.score() << std::endl;
}
return 0;
}
Error message when there is no default constructor,
Error:
required from 'static _ForwardIterator std::__uninitialized_default_n_1<_TrivialValueType>::__uninit_default_n(_ForwardIterator, _Size) [with _ForwardIterator = Foo*; _Size = long unsigned int; bool _TrivialValueType = false]'
Successful run output when there is default constructor,
In parametered constructor
In parametered constructor
In copy constructor
resizing begin
In copy constructor
student name: name1 student score: 4
The issue is your call to resize (specifically, students.resize(1)); when that line is removed, the code compiles. The issue is that resize has to initialize the new elements if it wasn't large enough, thus it needs the default constructor. If you want to shrink the size of your students without ensuring it's a sufficient size, you can use erase (Max Vollmer has a specific example).
Information on resize is available at cppreference. You're falling into the first (single argument) form.
Instead of students.resize(1);, which needs a default constructor, you can do students.pop_back();, or, if the size of your vector can vary, students.erase(students.begin()+1, students.end());.

Brace initialization prevents non-const use of temporary

I want to create a temporary copy of a const object and use it in a non-const way:
struct S {
S& f() { return *this; }
};
int main() {
const S a{};
S{a}.f(); // Error on this line
return 0;
}
Using msvc (Visual Studio 2017, C++14), I get this error:
Error C2662 'S &S::f(void)': cannot convert 'this' pointer from 'const S' to 'S &'
If I change the brace initialization to classic initialization, it works:
S{a}.f(); // Does not work
S(a).f(); // Works
Both variants compile fine in gcc. Am I missing something or is this a compiler bug?
Seems like another MSVC bug.
S{a} is deduced as const struct S, and that alone is bug.
#include <string>
#include <iostream>
template < class T >
std::string type_name()
{
std::string p = __FUNCSIG__;
return p.substr( 106, p.length() - 106 - 7 );
}
struct S {
S& f() { return *this; }
};
int main() {
const S a{};
//S{a}.f(); // Error on this line
std::cout << type_name<decltype(S{a})>() << std::endl;
return 0;
}
Output:
const struct S
It looks like a compiler bug, or the result of a weird optimization, because this variation of original code that only make ctors and dtor with side effects compiles fine using MSVC:
#include <iostream>
struct S {
S(const S& other) {
std::cout << "Copy ctor " << &other << " -> " << this << std::endl;
}
S() {
std::cout << "Default ctor " << this << std::endl;
}
~S() {
std::cout << "Dtor " << this << std::endl;
}
S& f() { return *this; }
};
int main() {
const S a{};
std::cout << &a << std::endl;
S{a}.f();
return 0;
}
Compilation is successful and output is:
Default ctor 0306FF07
Copy ctor 0306FF07 -> 0306FF06
Dtor 0306FF06
Dtor 0306FF07

OOP: Passing primitive types to function that expects objects

Is it possible to pass constructor arguments to a function instead of the class object itself ?
According to the following code
#include <iostream>
#include <string>
class CL{
public:
int id;
CL(){
std::cout << "CL () Constructor " << std::endl;
}
CL(const char * name){
std::cout << " CL(const char * name) Constructor Called " << std::endl;
}
CL(int i){
id = i;
std::cout << "CL(int i) Constructor Called " << id << std::endl;
}
void print(){
std::cout << "print method Called " << id << std::endl;
}
};
void myfunc(CL pp){
pp.print();
}
int main(int argc,char **argv){
myfunc(10);
}
I passed integer to the function "myfunc" instead of class instance and it worked. I think it instantiated object on the fly.
the output is
CL(int i) Constructor Called 10
print method Called 10
is it such an ambiguity ? as for the same code if I overloaded the function
"myfunc" as
myfunc(int i) {
std::cout << i << std::endl;
}
it will output
10
and ignore the function prototype that takes the class object
This is called implicit conversion and works for all constructors, that take a single parameter. In cases, where you don't want that, declare the constructor explicit:
class CL {
public:
int id;
CL(): id{0} {}
explicit CL(int i): id{i} {}
};
void myfunc(CL pp) {
// ...
}
int main(int, char) {
myfunc(10); // <- will fail to compile
}