So I have the following (cut down) classes:
template <typename A, typename... B>
struct ComponentTupleAccessor:
public ComponentArray<A>,
public ComponentTupleAccessor<B...>
{
ComponentTupleAccessor(const uint32_t capacity):
ComponentArray<A>(capacity),
ComponentTupleAccessor<B...>(capacity)
{}
};
template <typename A>
struct ComponentTupleAccessor<A>:
public ComponentArray<A>
{
ComponentTupleAccessor<A>(const uint32_t capacity):
ComponentArray<A>(capacity)
{}
};
template <typename A, typename ...B>
class ComponentTuple {
ComponentTupleAccessor<A, B...> m_Components;
uint32_t m_Capacity;
public:
ComponentTuple(const RB32u capacity):
m_Capacity{capacity},
m_Components(capacity)
{}
template <typename S, typename ...T>
void pop_back() {
m_Components.Component<S>::pop_back();
pop_back<T...>();
}
template <typename S>
void pop_back() {
m_Components.Component<S>::pop_back();
}
void pop_back() {
pop_back<A, B...>();
}
};
The ComponentArray
class is basically a wrapper around a vector that holds a bunch of components of a particular type.
The ComponentBlockTupleAccessor
class more or less emulates a cut down version of std::tuple where the any number of unique types of ComponentArray
can be inherited into the class ComponentTuple
using the variadic templates.
The pop_back
function in ComponentTuple
is designed to recursively pop_back
an element off each of the ComponentArray
s.
Outside of the ComponentTuple
class I'd like to be able to simply call something like compTupleInstance.pop_back()
and all ComponentArray
's should have their last elements removed.
I get a compile error "call of overloaded 'pop_back()' is ambiguous" pop_back();
I can't seem to figure out a combination of the A, B (pack), S, and T (pack) template parameters that gives me the functionality I need. What am I missing here?
Edit: Here is a simple usage scenario:
// ComponentTuple contains an int and a float ComponentArray with capacity 8.
ComponentTuple<int, float> dut(8);
// Push a set of new components to the ComponentArrays.
// This function has a similar structure to that of pop_back.
dut.push_back({8}, {3.141f});
// Another one
dut.push_back({4}, {2.718f});
// Remove the last element from all of the ComponentArrays.
dut.pop_back();
ComponentTuple template parameters will always be unique types, and there will always be greater than one.
A copy from the question:
template <typename S, typename ...T> // T can be empty
void pop_back() {
m_Components.Component<S>::pop_back();
pop_back<T...>();
}
template <typename S>
void pop_back() {
m_Components.Component<S>::pop_back();
}
If I invoke pop_back<A>()
I have S = A
. But, am I calling the first method with T
empty, or am I calling the second method?
The core issue: template <typename S, typename... T>
and template <typename S>
look equally good for the compiler when there is only one template argument (the pack can be empty). It cannot make a decision on which overload to use.
Solution: You can use fold expression (c++17 or above).
void pop_back() {
(m_Components.ComponentArray<A>::pop_back(), ... , m_Components.ComponentArray<B>::pop_back());
}
...Also the code breaks (even with the fold expression above) if used like this: ComponentTuple<int, int, double>
(ambiguous base class).
Thanks for your help guys, the ambiguity between <typename S>
and <typename S, typename... T>
seems obvious now that you've pointed it out. It seems it's just not possible to do it the way I was trying to, for that reason.
I ended up using a if constexpr
to test if the recursion is at the last Type, so I can terminate the recursion at that point. I like this even better since in the non templated pop_back
there is no risk of giving template parameters that weren't used in declaring the class. eg
ComponentTuple<int, float> dut(8);
push_back<Banana, char>; // Not int, float so should cause compile error.
Using the constexpr
method, I can privatise the pop_back
function (see pop_back_
below), and only expose the no-params version so that the method can only be called in the correct way:
template <typename A, typename ...B>
class ComponentTuple {
ComponentTupleAccessor<A, B...> m_Components;
uint32_t m_Capacity;
template <typename S, typename... T>
void pop_back_() {
m_Components.ComponentBlock<S>::pop_back();
if constexpr (sizeof...(T) > 0) {
pop_back_<T...>();
}
}
public:
ComponentTuple(const RB32u capacity):
m_Capacity{capacity},
m_Components(capacity)
{}
void pop_back() {
pop_back_<A, B...>();
}
};
Obviously I need to make sure I'm using at least c++17 to use if constexpr
.
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.