I have a template that is derived from either std::vector or std::list. I need to hide all methods that enable altering the size of the underlying container. I know that I can hide methods via the using declaration. The problem is that some relevant methods are only available for std::list. Is it possible to conditionally hide those methods without explicitly overloading them (which would be a hassle)? Is there some fancy std::enable_if syntax that I can apply to the using declaration as well?
template <class ValueType, class BaseType = std::vector<ValueType>,
std::enable_if_t<std::is_same_v<BaseType, std::vector<ValueType>> || std::is_same_v<BaseType, std::list<ValueType>>,int> = 0
>
struct Test : public BaseType
{
Test() = default;
private:
// Hide base members that are not supposed to be used directly
using BaseType::push_back;
using BaseType::insert;
using BaseType::assign;
using BaseType::emplace_back;
using BaseType::resize;
using BaseType::swap;
// These methods must be hidden BaseType==std::list, but are not available for std::vector
//using BaseType::merge;
//using BaseType::push_front;
//using BaseType::splice;
//using BaseType::emplace_front;
}
As a workaround, I could write new base class wrappers that are selected via std::conditional, like this, but it is surely not too nice:
template<class T>
struct CVector : public std::vector<T>
{
using std::vector<T>::vector;
protected:
using std::vector<T>::assign;
using std::vector<T>::emplace_back;
using std::vector<T>::insert;
using std::vector<T>::push_back;
using std::vector<T>::resize;
using std::vector<T>::swap;
};
template <class T>
struct CList : public std::list<T>
{
using std::list<T>::list;
protected:
using std::list<T>::assign;
using std::list<T>::emplace_back;
using std::list<T>::insert;
using std::list<T>::push_back;
using std::list<T>::resize;
using std::list<T>::swap;
using std::list<T>::emplace_front;
using std::list<T>::merge;
using std::list<T>::push_front;
using std::list<T>::splice;
};
template <class ValueType, class BaseType = std::vector<ValueType>,
std::enable_if_t<std::is_same_v<BaseType, std::vector<ValueType>> || std::is_same_v<BaseType, std::list<ValueType>>,int> = 0
>
struct Test : public std::conditional<std::is_same_v<BaseType, std::vector<ValueType>>, CVector<ValueType>, CList<ValueType>>::type
{
Test() = default;
}
You could simplify it somewhat by making specializations of a base class template:
template<class>
struct Base; // not implemented for the types you don't support
template <class T, class Allocator>
struct Base<std::vector<T, Allocator>> : std::vector<T, Allocator> {
using std::vector<T, Allocator>::vector;
protected:
using std::vector<T, Allocator>::assign;
using std::vector<T, Allocator>::emplace_back;
using std::vector<T, Allocator>::insert;
using std::vector<T, Allocator>::push_back;
using std::vector<T, Allocator>::resize;
using std::vector<T, Allocator>::swap;
};
template <class T, class Allocator>
struct Base<std::list<T, Allocator>> : std::list<T, Allocator> {
using std::list<T, Allocator>::list;
protected:
using std::list<T, Allocator>::assign;
using std::list<T, Allocator>::emplace_back;
using std::list<T, Allocator>::insert;
using std::list<T, Allocator>::push_back;
using std::list<T, Allocator>::resize;
using std::list<T, Allocator>::swap;
using std::list<T, Allocator>::emplace_front;
using std::list<T, Allocator>::merge;
using std::list<T, Allocator>::push_front;
using std::list<T, Allocator>::splice;
};
The usage would then be simpler:
template<class T>
struct Test : Base<T> {
Test() = default;
};
int main() {
Test<std::vector<int>> v;
Test<std::list<int>> l;
}
or if you wish:
template<class T, class BaseType = std::vector<T>>
struct Test : Base<BaseType> {
Test() = default;
};
int main() {
Test<int> v;
Test<int, std::list<int>> l;
}
or if you don't want to repeat the value type when specifying the container:
template <class T, template <class, class> class BaseType = std::vector,
class Allocator = std::allocator<T>>
struct Test : Base<BaseType<T, Allocator>> {
Test() = default;
};
int main() {
Test<int> v;
Test<int, std::list> l; // note: `int` not needed
}