As a hypothetical question, I would like to use lambdas as class methods. I understand this is bad in a professional context, but I'm curious anyway. An example would probably be best for showing what I want to do. Here is a basic class for complex numbers:
class Complex {
private:
double re, im;
public:
Complex() : re(0.0), im(0.0) {}
Complex(double re, double im) : re(re * 1.0), im(im * 1.0) {}
Complex(const Complex &c) = default;
~Complex() = default;
function<double(void)> getRe = [=]() -> double { return re; };
function<void(double)> setRe = [&](double re) -> void { this->re = re; };
function<double(void)> getIm = [=]() -> double { return im; };
function<void(double)> setIm = [&](double im) -> void { this->im = im; };
};
At first I have tried using auto
instead of explicitly specifying function types but I got errors saying that I cant use auto
in non static fields.
This seems to actually work, as in it apparently produces the desired behaviour. I have used it to draw some fractals using OpenGL so it ends up doing some fairly intensive work.
As I said it seems to work, I have used reference capture for the setters especially since I figured that since this
is a reference to the current instance it might be needed, and value capture for the getters since by default an identifier ends up searching(in this case) in the class scope and finds the fields.
I have 2 questions:
I have not tested this with visual studio but in what I am using, CLion with the MSVC compiler, the this
is highlighted as as an inexistent variable(even though it "works"). Any ideas why this happens?
The class ends up being slow. As in more than an order of magnitude slower. The rendering goes from being absolutely instant when I use plain getters and setters like double getRe() {return re;}
, to taking 2-3 seconds. Why does this happen?
Lambdas are now allowed inside constexpr functions.
Copying a lambda will copy its state.
From the various lambda improvements, template parameters for lambdas are my favorite ones. Lambdas support with C++20 template parameters, can be default-constructed and support copy-assignment, when they have no state, and can be used in unevaluated contexts.
Calls of the lambda are translated to direct calls to its operator() and can therefore be inlined.
I like the idea, but it doesn't work out so well in practice.
std::function
is a class type, like any other custom class you might write. sizeof(std::function)
varies from implementation to implementation, but a reasonable value is 24
bytes1. This means that you'd be adding 24
bytes to sizeof(Complex)
for every member std::function
you want to add, compared to adding 0
bytes for every member function. Compared to sizeof(double) == 8
on most machines, that's a lot of overhead: your Complex
type could be 16
bytes but is instead roughly 112
bytes.
Furthermore, every std::function
member has to be initialized, possibly requiring a heap allocation, and calling a std::function
involves virtual functions (or equivalent) because of type erasure. This makes it really hard for the compiler to optimize and nearly impossible for the compiler to inline the functions, whereas the regular member functions are almost guaranteed to be inlined due to how simple they are.
Using std::function
for member functions means that your type is uselessly bigger, takes more work to initialize, and is much harder to optimize. That's why it's so much slower.
1: At this time, sizeof(std::function)
is actually 32
bytes, 48
bytes, and 64
bytes on libstdc++, libc++, and MSVC's STL respectively
To avoid the per-object overhead, you could have static constexpr
members (at least in C++17), but then you'd have to have an explicit this
parameter, which removes all the nice sugar that member functions have. You'd have to write Complex::getRe(myComplex)
rather than myComplex.getRe()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With