std::map::clear and elements' destructors - c++

Does destructor get called on std::map elements when std::map::clear is used?
I tried to debug for std::map<string,string> but could not see std::string destructor getting invoked. Can any one please help my understanding?
Documentation states it gets called, but I could not notice it.

Documentation is right, it does get called.
The destruction will be done by the method std::allocator<T>::deallocate(). Trace through that in your debugger.
http://www.cplusplus.com/reference/std/memory/allocator/

The destructor does get called. Here is an example to illustrate:
#include <iostream>
#include <map>
class A
{
public:
A() { std::cout << "Constructor " << this << std::endl; }
A(const A& other) { std::cout << "Copy Constructor " << this << std::endl; }
~A() { std::cout << "Destructor " << this <<std::endl; }
};
int main()
{
std::map<std::string, A> mp;
A a;
mp.insert(std::pair<std::string, A>("hello", a));
mp.clear();
std::cout << "Ending" << std::endl;
}
This will report an output similar to this:
Constructor 0xbf8ba47a
Copy Constructor 0xbf8ba484
Copy Constructor 0xbf8ba48c
Copy Constructor 0x950f034
Destructor 0xbf8ba48c
Destructor 0xbf8ba484
Destructor 0x950f034
Ending
Destructor 0xbf8ba47a
So, you can see that the destructors get called by the calling the clear function.

try with a std::map<A,B> where A and B are custom types that have a destructor in which you have set a breakpoint. You will see that it does get invoked, and exactly what scope this destruction happens at.

Here is a bit more of a complete test, building on Chris Mansley's code as I wanted to see the effect on values, ptrs and refs - and I wanted to see the difference
between clear and erase. No difference. In summary the destructor is only called
for value types, which you would expect. I just like to check my understanding 8)
#include <iostream>
#include <map>
class A
{
public:
std::string some_data;
A(std::string some_data) : some_data(some_data) {
std::cout << " A(" << some_data << ") #" << this << std::endl;
}
A(const A& other) {
some_data = other.some_data;
std::cout << " Copy A(" << other.some_data << ") #" << this << std::endl;
}
~A() {
std::cout << " Destruct ~A(" << some_data << ") #" << this << std::endl;
}
};
void clear_test_value (void)
{
std::cout << "clear_test_value() {" << std::endl;
std::map<std::string, A> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A>("key1", a));
mp.clear();
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void erase_test_value (void)
{
std::cout << "erase_test_value() {" << std::endl;
std::map<std::string, A> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A>("key2", a));
auto f = mp.find("key2");
if (f == mp.end()) {
std::cout << "failed to find element {" << std::endl;
return;
}
mp.erase(f);
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void clear_test_ptr (void)
{
std::cout << "clear_test_ptr() {" << std::endl;
std::map<std::string, A*> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A*>("key1", &a));
mp.clear();
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void erase_test_ptr (void)
{
std::cout << "erase_test() {" << std::endl;
std::map<std::string, A*> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A*>("key2", &a));
auto f = mp.find("key2");
if (f == mp.end()) {
std::cout << "failed to find element {" << std::endl;
return;
}
mp.erase(f);
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void clear_test_ref (void)
{
std::cout << "clear_test_ref() {" << std::endl;
std::map<std::string, A&> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A&>("key1", a));
mp.clear();
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void erase_test_ref (void)
{
std::cout << "erase_test_ref() {" << std::endl;
std::map<std::string, A&> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A&>("key2", a));
auto f = mp.find("key2");
if (f == mp.end()) {
std::cout << "failed to find element {" << std::endl;
return;
}
mp.erase(f);
std::cout << "}" << std::endl;
std::cout << std::endl;
}
int main ()
{
clear_test_value();
erase_test_value();
clear_test_ptr();
erase_test_ptr();
clear_test_ref();
erase_test_ref();
return (0);
}
Output:
clear_test_value() {
A(A1 data) #0x7ffee07389a0
Copy A(A1 data) #0x7ffee0738960
Copy A(A1 data) #0x7fe98fc029c8
Destruct ~A(A1 data) #0x7ffee0738960
Destruct ~A(A1 data) #0x7fe98fc029c8
}
Destruct ~A(A1 data) #0x7ffee07389a0
erase_test_value() {
A(A1 data) #0x7ffee07387f0
Copy A(A1 data) #0x7ffee07387b0
Copy A(A1 data) #0x7fe98fc029c8
Destruct ~A(A1 data) #0x7ffee07387b0
Destruct ~A(A1 data) #0x7fe98fc029c8
}
Destruct ~A(A1 data) #0x7ffee07387f0
clear_test_ptr() {
A(A1 data) #0x7ffee07389b0
}
Destruct ~A(A1 data) #0x7ffee07389b0
erase_test() {
A(A1 data) #0x7ffee0738800
}
Destruct ~A(A1 data) #0x7ffee0738800
clear_test_ref() {
A(A1 data) #0x7ffee07389b0
}
Destruct ~A(A1 data) #0x7ffee07389b0
erase_test_ref() {
A(A1 data) #0x7ffee0738800
}
Destruct ~A(A1 data) #0x7ffee0738800

Related

Proper handling of member function pointers

I wrote a generic class for handling and executing a function pointer. This is a simplified equivalent of std::function and std::bind. To handle member functions I use cast to internal EventHandler::Class type. Question: is it ok to cast it that way? Will it work in all cases when invoking handled function?
template <typename ReturnType, typename... Arguments>
class EventHandler
{
class Class {};
ReturnType (Class::*memberFunction)(Arguments...) = nullptr;
union {
Class *owner;
ReturnType(*function)(Arguments...) = nullptr;
};
public:
EventHandler() = default;
EventHandler(EventHandler &&) = default;
EventHandler(const EventHandler &) = default;
EventHandler &operator=(EventHandler &&) = default;
EventHandler &operator=(const EventHandler &) = default;
EventHandler(ReturnType (*function)(Arguments...)) :
function(function)
{
}
template <typename Owner>
EventHandler(Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...)) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
template <typename Owner>
EventHandler(const Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...) const) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
ReturnType operator()(Arguments... arguments)
{
return memberFunction ?
(owner ? (owner->*memberFunction)(arguments...) : ReturnType()) :
(function ? function(arguments...) : ReturnType());
}
};
The implementation provides handle for a global function, a member function and a const member function. Obviously there is volatile and const volatile that is not show here for clarity.
EDIT
All the code below is just a representation of all of kinds of supported functions.
class Object
{
public:
double y = 1000;
Object() = default;
Object(double y) : y(y) {}
static void s1(void) { std::cout << "s1()" << std::endl; }
static void s2(int a) { std::cout << "s2(a:" << 10 + a << ")" << std::endl; }
static void s3(int a, float b) { std::cout << "s3(a:" << 10 + a << ", b:" << 10 + b << ")" << std::endl; }
static int s4(void) { std::cout << "s4(): "; return 10 + 4; }
static Object s5(int a) { std::cout << "s5(a:" << 10 + a << "): "; return Object(10 + 5.1); }
static float s6(int a, Object b) { std::cout << "s6(a:" << 10 + a << ", b:" << 10 + b.y << "); "; return 10 + 6.2f; }
void m1(void) { std::cout << "m1()" << std::endl; }
void m2(int a) { std::cout << "m2(a:" << y + a << ")" << std::endl; }
void m3(int a, float b) { std::cout << "m3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int m4(void) { std::cout << "m4(): "; return ((int) y) + 4; }
Object m5(int a) { std::cout << "m5(a:" << y + a << "): "; return Object(y + 5.1); }
float m6(int a, Object b) { std::cout << "m6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
void c1(void) const { std::cout << "c1()" << std::endl; }
void c2(int a) const { std::cout << "c2(a:" << y + a << ")" << std::endl; }
void c3(int a, float b) const { std::cout << "c3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int c4(void) const { std::cout << "c4(): "; return ((int) y) + 4; }
Object c5(int a) const { std::cout << "c5(a:" << y + a << "): "; return Object(y + 5.1); }
float c6(int a, Object b) const { std::cout << "c6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
};
void f1(void) { std::cout << "f1()" << std::endl; }
void f2(int a) { std::cout << "f2(a:" << a << ")" << std::endl; }
void f3(int a, float b) { std::cout << "f3(a:" << a << ", b:" << b << ")" << std::endl; }
int f4(void) { std::cout << "f4(): "; return 4; }
Object f5(int a) { std::cout << "f5(a:" << a << "): "; return Object(5.1); }
float f6(int a, Object b) { std::cout << "f6(a:" << a << ", b:" << b.y << "); "; return 6.2f; }
Here is the usage example for all of the above functions
int main()
{
std::cout << "=== Global functions" << std::endl;
EventHandler ef1(f1); ef1();
EventHandler ef2(f2); ef2(2);
EventHandler ef3(f3); ef3(3, 3.1f);
EventHandler ef4(f4); std::cout << ef4() << std::endl;
EventHandler ef5(f5); std::cout << ef5(5).y << std::endl;
EventHandler ef6(f6); std::cout << ef6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member static functions" << std::endl;
EventHandler es1(Object::s1); es1();
EventHandler es2(Object::s2); es2(2);
EventHandler es3(Object::s3); es3(3, 3.1f);
EventHandler es4(Object::s4); std::cout << es4() << std::endl;
EventHandler es5(Object::s5); std::cout << es5(5).y << std::endl;
EventHandler es6(Object::s6); std::cout << es6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member functions" << std::endl;
Object object(20);
EventHandler em1(&object, &Object::m1); em1();
EventHandler em2(&object, &Object::m2); em2(2);
EventHandler em3(&object, &Object::m3); em3(3, 3.1f);
EventHandler em4(&object, &Object::m4); std::cout << em4() << std::endl;
EventHandler em5(&object, &Object::m5); std::cout << em5(5).y << std::endl;
EventHandler em6(&object, &Object::m6); std::cout << em6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member const functions" << std::endl;
const Object constObject(30);
EventHandler ec1(&constObject, &Object::c1); ec1();
EventHandler ec2(&constObject, &Object::c2); ec2(2);
EventHandler ec3(&constObject, &Object::c3); ec3(3, 3.1f);
EventHandler ec4(&constObject, &Object::c4); std::cout << ec4() << std::endl;
EventHandler ec5(&constObject, &Object::c5); std::cout << ec5(5).y << std::endl;
EventHandler ec6(&constObject, &Object::c6); std::cout << ec6(6, Object(6.1)) << std::endl;
system("pause");
return 0;
}
Finally - to the point - here an example that shows how much easier in use is the EventHandler I prepared when compared to std::function interface. And actually the reason of such approach.
EventHandler<float, int, Object> example;
example = f6;
example(7, Object(7.1));
example = EventHandler(&object, &Object::m6);;
example(8, Object(8.1));
It’s undefined behavior to call a function through a function pointer(-to-member) of a different type. (Some practical reasons for this rule are that the object’s address might need to be adjusted to call a member function of a base class or that a vtable might be involved.) You can use type erasure to allow calling member functions on objects of different types (which is what std::bind does), or you can (restrict to member functions and) add the class type as a template parameter.
Of course, the usual answer is to just use std::function with a lambda that captures the object in question and calls whatever member function. You can also take the C approach and define various functions with a void* parameter that cast that parameter to a known class type and call the desired member function.

mutable object referred by std::any

Is only the way to modify, not replace, an object stored as std::any is to declare changeable data mutable? E.g. to avoid creation and copy of class S instances:
#include <iostream>
#include <vector>
#include <any>
#include <string>
struct S {
mutable std::string str;
S(S&& arg) : str(std::move(arg.str)) { std::cout << ">> S moved" << std::endl; }
S(const S& arg) : str(arg.str) { std::cout << ">> S copied" << std::endl; }
S(const char *s) : str(s) { std::cout << ">> S created" << std::endl; }
S& operator= (const S& arg) { str = arg.str; return *this; }
S& operator= (S&& arg) { str = std::move(arg.str); return *this; }
virtual ~S() { std::cout << "<< S destroyed" << std::endl; }
};
int main() {
std::vector<std::any> container;
container.emplace_back(S("Test 1"));
std::any_cast<const S&>(container[0]).str = "Test 2";
for (const auto& a : container) {
std::cout << a.type().name() << ", "
<< std::any_cast<const S&>(a).str << std::endl;
}
}
You can any_cast with non-const reference:
std::any_cast<S&>(container[0]).str = "Test 2";
Demo
or
std::any a = 40;
std::any_cast<int&>(a) += 2;
std::cout << std::any_cast<int>(a) <<std::endl;
Demo
nope.
The issue is here.
std::any_cast<const S&>(container[0]).str = "Test 2";
you are trying to change constant data

Is there a way to avoid calling constructor and destructor for perfect forwarding to a function?

I am trying to pass literal to a perfect forwarding setter, but this results in object construction, movement and destruction instead. I am looking for a more efficient way to do this.
struct TStruct {
TStruct(int va) : a(va) {
std::cout << "- TStruct(" << va << ")" << std::endl;
}
TStruct(const TStruct& other) :
a(other.a)
{
std::cout << "- TStruct(const TStruct& )" << std::endl;
}
TStruct(TStruct&& other) :
a(std::exchange(other.a, 0))
{
std::cout << "- TStruct(const TStruct&&)" << std::endl;
}
TStruct& operator=(const TStruct& rhs) {
std::cout << "- TStruct operator= const& " << std::endl;
// check for self-assignment
if(&rhs == this) return *this;
a = rhs.a;
return *this;
}
TStruct& operator=(TStruct&& rhs) {
std::cout << "- TStruct operator=&&" << std::endl;
// check for self-assignment
if(&rhs == this) return *this;
a = std::exchange(rhs.a, 0);
return *this;
}
~TStruct() {
std::cout << "~ TStruct() destructor with " << a << std::endl;
}
int a = 1;
};
struct TPerfectForward {
template<class T>
TPerfectForward(T&& tsv) : ts(std::forward<T>(tsv)) {}
template<class T>
void set(T&& tsv) { ts = std::forward<T>(tsv); }
TStruct ts;
};
std::cout << "TPerfectForward (5)" << std::endl;
TPerfectForward pf(5);
std::cout << "TPerfectForward set(4)" << std::endl;
pf.set(4);
This gives following results with gcc 7.4.0 on Ubuntu:
TPerfectForward (5);
- TStruct(5)
TPerfectForward set(4)
- TStruct(4)
- TStruct operator=&&
~ TStruct() destructor with 0
I would like these results better:
TPerfectForward (5)
- TStruct(5)
TPerfectForward set(4)
- TStruct(4)
Is there a way to avoid calling move operator and destructor when perfect forwarding to a function?

C++ pointer to own struct member has different value after reassignment

I'm using Visual Studio 2017 and came across a bug which I cannot understand. I have this code as an example:
#include <iostream>
struct Foo
{
int a = 0;
int c;
int *b;
Foo(int n)
{
a = n;
b = &c;
}
};
struct Bar
{
Foo nestedFoo = Foo(0);
Bar(int n)
{
std::cout << "bar constructor nestedFoo before:" << std::endl;
std::cout << &nestedFoo.a << std::endl;
std::cout << nestedFoo.b << std::endl;
nestedFoo = Foo(n);
std::cout << "bar constructor nestedFoo after:" << std::endl;
std::cout << &nestedFoo.a << std::endl;
std::cout << nestedFoo.b << std::endl;
}
};
struct App
{
Bar bar = Bar(2);
};
int main()
{
App app;
Bar bar = Bar(10);
Foo foo = Foo(5);
std::cout << "foo before:" << std::endl;
std::cout << &foo.a << std::endl;
std::cout << foo.b << std::endl;
foo = Foo(20);
std::cout << "foo after:" << std::endl;
std::cout << &foo.a << std::endl;
std::cout << foo.b << std::endl;
std::cout << "bar.nestedFoo:" << std::endl;
std::cout << &bar.nestedFoo.a << std::endl;
std::cout << bar.nestedFoo.b << std::endl;
std::cout << "app.bar.nestedFoo:" << std::endl;
std::cout << &app.bar.nestedFoo.a << std::endl;
std::cout << app.bar.nestedFoo.b << std::endl;
return 0;
}
I expected the pointer and the address of the member to be the same after the reassignment but it seems this is not the case, &Foo.a and Foo.b have different values:
bar constructor nestedFoo before:
0058FEE8
0058FEE8
bar constructor nestedFoo after:
0058FEE8
0058FC24
bar constructor nestedFoo before:
0058FED8
0058FED8
bar constructor nestedFoo after:
0058FED8
0058FD04
foo before:
0058FEC8
0058FEC8
foo after:
0058FEC8
0058FDF8
bar.nestedFoo:
0058FED8
0058FD04
app.bar.nestedFoo:
0058FEE8
0058FC24
At first I thought it only happened when I had Foo as a member of another struct, but it seems to happen even at a top-level assignment. So what am I doing wrong?
Any help will be appreciated, thank you!
Edit: showing that assigning b to &c which is a member that doesn't get (explicitly) reassigned also doesn't work.

How Can I avoid implicit move constructor inside a lambda function

I am using "emplace" method to avoid memory copy.
But, when I am using the "emplace" inside a Lambda function. It always call implicit move constructor.
How I can avoid memory copy inside a Lambda function?
This sample program should not print “I am being moved.”
#include <vector>
#include <iostream>
struct A
{
int a;
A(int t) : a(t)
{
std::cout << "I am being constructed.\n";
}
A(A&& other) : a(std::move(other.a))
{
std::cout << "I am being moved.\n";
}
};
std::vector<A> g_a;
int main()
{
std::cout << "emplace_back:\n";
g_a.emplace_back(1);
std::cout << "emplace_back in lambda:\n";
auto f1 = [](int x) { g_a.emplace_back(x); };
f1(2);
std::cout << "\nContents: ";
for (A const& t : g_a)
std::cout << t.a << " ";
std::cout << std::endl;
}
It's not about the lambda function but rather about the vector that reallocates its memory. You can ammend this with std::vector::reserve.
int main() {
g_a.reserve(10);
^^^^^^^^^^^^^^^^
std::cout << "emplace_back:\n";
g_a.emplace_back(1);
std::cout << "emplace_back in lambda:\n";
auto f1 = [](int x) { g_a.emplace_back(x); };
f1(2);
std::cout << "\nContents: ";
for (A const& t : g_a)
std::cout << t.a << " ";
std::cout << std::endl;
}
Live Demo