简体   繁体   中英

How to store a function object without resorting to std::function?

I am trying to make a generic container that would hold objects and their position:

class Vector;

template <typename T>
class Container 
{
public:
    void insert(const T& t)
    {
        insertAtPosition(t.getPosition() ,t);
    }
private:
    void insertAtPosition(const Vector& v, const T& t);
    ...
} ;

But what if the users' object position getter is not called getPosition ?

How can I make this container generic with respect to the way in which the container internally obtains the position of an item?

So far, I have considered 3 approaches, none of them ideal:

  1. Add a std::function<const Vector& (const T& t)> member to the Container .

This is a clean C++ solution, but this function is going to be called very very often and it may result in noticeable performance decrease.

  1. Add a functor object to the container:

     class Vector; template <typename T, typename TGetPosition> class Container { public: Container(TGetPosition getPosition): getPosition_(getPosition){} void insert(const T& t) { insertAtPosition(getPosition_(t) ,t); } private: void insertAtPosition(const Vector& v, const T& t); TGetPosition getPosition_; } ; 

I can use the object generator idiom to make it possible to use lambdas:

template <typename T, typename TGetPosition>
Container<T, TGetPosition> makeContainer(TGetPosition getter)
{
    return Container<T, TGetPosition>(getter);
}

...

auto container = makeSimpleContainer<Widget>([](const Widget& w)
    {
        return w.tellMeWhereYourPositionMightBe();
    });

There would be no performance overhead, but it would be impossible to get the type of such a container in certain contexts. For example, you could not create a class that would take such a container as a parameter, since decltype would not work, because lambdas cannot be used in unevaluated contexts.

  1. Use #define GETTER getPosition and the user would just change getPosition to whatever he likes. There are so many things wrong with this approach that I don't even know where to start.

Please, is there some other way to do this? Did I miss anything. Any guidance is most welcome!

EDIT:

Regarding solution 2: I have no idea how could we get a the type of a container created with a lambda function. One way would be:

using TContainer = decltype(makeSimpleContainer<Widget>([](const Widget& w)
    {
        return w.tellMeWhereYourPositionMightBe();
    });)

But this doesn't work, because lambdas cannot be used in unevaluated contexts.

Reasonably usable option would be to expect the context to have position_for() :

template <class T> struct Container {
    size_t insert(T const& x) {
        insertAtPosition(position_for(x), x);
    }
};

Vector const& position_for(const Widget& w) {
    return ...;
}

Container<Widget> c;
c.insert(Widget());

...but designs where the container generates key from the business object usually do not fly well, as to lookup the object one would need to do a dummy one, which may be expensive.

It seems like your second solution will work even if you need to make a class holding a container:

template <typename Container>
struct SomeClass {
    SomeClass(const Container &container) : container(container) { }
    Container container;
};


int main()
{
    auto container = makeSimpleContainer<Widget>([](const Widget& w)
        {
            return w.tellMeWhereYourPositionMightBe();
        });
    SomeClass<decltype(container)> test(container);
}

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