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