Live Hacking: Handwritten Functors, And Capturing By Reference (Pitfalls)¶
Original, Handwritten Functor (By Copy)¶
#include <functional>
#include <string>
#include <iostream>
class print_message_func
{
public:
print_message_func(const std::string& message) : _message(message) {}
void operator()()
{
std::cout << _message << std::endl;
}
private:
std::string _message;
};
static std::function<void()> create_print_function(const std::string& message)
{
auto print_message = print_message_func(message);
return print_message;
}
int main()
{
auto p = create_print_function("howdy");
p();
return 0;
}
$ code/c++11-lambda-capture-handwritten
howdy
Possible Pitfall: Capturing A Reference¶
#include <functional>
#include <string>
#include <iostream>
class print_message_func
{
public:
print_message_func(const std::string& message) : _message(message) {}
void operator()()
{
std::cout << _message << std::endl; // using reference! STILL VALID?
}
private:
const std::string& _message;
};
static std::function<void()> create_print_function(const std::string& message)
{
auto print_message = print_message_func(message);
return print_message;
}
int main()
{
auto p = create_print_function("howdy"); // <--- temporary std::string object; LIFETIME?
p();
return 0;
}
$ code/c++11-lambda-capture-handwritten-reference
howdy
Program creates a temporary
std::string
object ("howdy"
)Functor stores a reference to it, and uses it later
⟶ Attentive programmer jumps up and shouts!
Attention
Nothing happens though (pity!)
⟶ lets dissect this for a moment …
Lifetime Of Temporary, Made Explicit¶
Replace
std::string
with custom classDebug output in ctor and dtor
Stores only pointer to
"howdy"
#include <functional>
#include <string>
#include <iostream>
struct String
{
String(const char* s) : s(s) { std::cout << "ctor" << std::endl; }
~String() { std::cout << "dtor" << std::endl; }
const char* s;
};
class print_message_func
{
public:
print_message_func(const String& message) : _message(message) {}
void operator()()
{
std::cout << _message.s << std::endl; // using reference! STILL VALID?
}
private:
const String& _message;
};
static std::function<void()> create_print_function(const String& message)
{
auto print_message = print_message_func(message);
return print_message;
}
int main()
{
auto p = create_print_function("howdy"); // <--- temporary std::string object; LIFETIME?
p();
return 0;
}
$ code/c++11-lambda-capture-handwritten-reference-gosh
ctor
dtor
howdy
Danger
Obviously String
instance destroyed before use!
Lifetime Of Temporary, Made Explicit ⟶ 🚑¶
Modify custom
String
Allocate and deallocate memory to hold string
Talk about resource management, object copy, and self assignment (gosh!)
#include <functional>
#include <string>
#include <cstring>
#include <iostream>
struct String
{
String(const char* s)
: s(new char[strlen(s)+1])
{
strcpy(this->s, s);
std::cout << "ctor" << std::endl;
}
~String()
{
std::cout << "dtor" << std::endl;
delete[] s;
}
char* s;
};
class print_message_func
{
public:
print_message_func(const String& message) : _message(message) {}
void operator()()
{
std::cout << _message.s << std::endl; // using reference! STILL VALID?
}
private:
const String& _message;
};
static std::function<void()> create_print_function(const String& message)
{
auto print_message = print_message_func(message);
return print_message;
}
int main()
{
auto p = create_print_function("howdy"); // <--- temporary std::string object; LIFETIME?
p();
return 0;
}
$ code/c++11-lambda-capture-handwritten-reference-gosh-boom
ctor
dtor
�
$ valgrind code/c++11-lambda-capture-handwritten-reference-gosh-boom
... hell ...