Reputation: 13313
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:
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.
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.
#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.
Upvotes: 1
Views: 130
Reputation: 18864
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.
Upvotes: 1
Reputation: 64308
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);
}
Upvotes: 1