问题描述:

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);
}
相关阅读:
Top