Reputation: 1747
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?
Upvotes: 9
Views: 8411
Reputation: 25277
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()
Upvotes: 7
Reputation: 62553
Let me fix this code for you:
struct Complex {
double re, im;
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;
};
Exactly the same outcome, better readability, smaller memory footprint. Go for it. You code is also pretty terrible for const-correctness.
And yes, your code is so terribly slow because of multiple factors:
Upvotes: -2