简体   繁体   中英

Can a std::function member have access to other members?

I am making a small tower defence game in order to practice C++. I know other languages, but have done little actual development in C++. In this game I'll have several buildings, so I made a class like so:

class Building
{
private:
    sf::Sprite& sprite;
    float timer = 0;
    void update(float delta);
};

Now, I'll have many different buildings, but they should all have the same members. Now, in Java I would (have to) make a subclass for each different type of building, bu I was thinking, can I change void update(float delta); to std::function<void(float)> update; and have factory funtions to create the different buildings? What I imagine is something like this:

In building.h:

class Building
{
private:
    sf::Sprite& sprite;
    float timer = 0;
    std::function<void(float)> update;
};

And in building.cpp

Building getBuildingA()
{
    Building b;
    b.sprite = /* ... */
    b.update = [](float delta) {
        std::cout << timer++ << std::endl;
    };
    return b;
}

But of course, I cannot actually access the variable timer there. Is there a way I can do something similar?

You can make the lambda capture this . A better solution is probably something like

class Building
{
private:
    sf::Sprite& sprite;
    float timer = 0;
    std::function<void(Building*, float)> update_impl;
    void update(float x) { update_impl(this, x); }
};

But really, based on this fragment of your design it looks like you really should be using subclasses and virtual functions; this is the sort of thing they're meant for.

The typical application of function is when you pass function objects around -- eg to replace the Java paradigm of writing an interface class with a single method, especially in settings when you'd rather just write functions or lambdas rather than whole objects.

For object polymorphism, the main alternative to subclassing and virtual functions is to use templates and generic programming -- although that's mainly for when you need polymorphism to happen at compile time . (which, I imagine, is inappropriate for your circumstance)

You can simply capture this .
As an example:

struct S {
    auto foo() {
        return [this](){ this->bar(); };
    }

    void bar() { }
};

int main() {
    S s;
    s.foo()();
}

If you are using C++14, you can use an initializer list instead.
As an example:

#include<cassert>

struct S {
    auto foo() {
        return [timer = timer](){
            assert(timer == 42);
        };
    }

    int timer{42};
};

int main() {
    S s{};
    s.foo()();
}

This way, you'll get a copy-initialized variable named timer to be used within the body of the lambda function.

You can work around the privacy issue by using a static-member function of Building to provide the factory class.

A secondary issue you're going to run into is that you're returning a Building by value, so, unless RVO happens you won't know the ultimate address of the object you are returning. You can work around this by having your "update()" function pass it's "this" as a parameter.

#include <iostream>
#include <functional>

class Building {
    // C++ classes are private by default
    int timer_;
    std::function<void(Building*)> update_;

public:
    void update() {
        update_(this);
    }

    static Building getBuildingA() {
        Building b;
        b.timer_ = 42;
        b.update_ = [](Building* b) {
            b->timer_++;
            std::cout << b->timer_ << '\n';
        };
    }
};

int main() {
    Building b = Building::getBuildingA();
    b.update();
    return 0;
}

Working demo: http://ideone.com/1Jy6zI

But also be aware that you cannot assign a reference to initialize it, you must initialize it. Also be aware that C++ is not ref-counted by default, so your reference to the sprite can easily become a dangling reference.

You might want to consider a std::shared_ptr instead of a reference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM