xerion xerion - 2 months ago 16
C++ Question

Add operator[] for vector when Type is unique_ptr

Suppose that we have the vector class below which has been shortened to minimum to showcase the question.

template <typename T>
class VectorT : private std::vector<T>
{
using vec = std::vector<T>;
public:
using vec::operator[];
using vec::push_back;
using vec::at;
using vec::emplace_back;

// not sure if this is the beast way to check if my T is really a unique_ptr
template<typename Q = T>
typename Q::element_type* operator[](const size_t _Pos) const { return at(_Pos).get(); }
};


Is there any way to check if T is a unique_ptr and if yes to add an operator[] to return the unique_ptr::element_type*. At the same time though the normal operator[] should also work.

VectorT<std::unique_ptr<int>> uptr_v;
uptr_v.emplace_back(make_unique<int>(1));
//int* p1 = uptr_v[0]; // works fine if using vec::operator[]; is commented out
// then of course it wont work for the normal case
//std::cout << *p1;

VectorT<int*> v;
v.emplace_back(uptr_v[0].get());
int *p2 = v[0];
std::cout << *p2;


Any way to achieve something like that ?

Edited:

The reason I am asking for this is cause I can have say my container

class MyVec: public VectorT<std::unique_ptr<SomeClass>>


but I can also have a

class MyVecView: public VectorT<SomeClass*>


Both classes will pretty much have identical functions. So I am trying to avoid duplication by doing something like

template<typename T>
void doSomething(VectorT<T>& vec)
{
SomeClass* tmp = nullptr;

for (size_t i = 0; i < vec.size(); ++i)
{
tmp = vec[i]; // this has to work though
....
}
}


Then of course I can

MyVec::doSomething(){doSomething(*this);}
MyVecView::doSomething(){doSomething(*this);}


which of course means that the
operator[]
has to work for both cases

Answer
template<typename T> struct unique_ptr_type { };
template<typename T> struct unique_ptr_type<std::unique_ptr<T>> { using type = T; };

namespace detail {
    template<typename T> std::false_type is_unique_ptr(T const&);
    template<typename T> std::true_type is_unique_ptr(std::unique_ptr<T> const&);
}
template<typename T>
using is_unique_ptr = decltype(detail::is_unique_ptr(std::declval<T>()));

template<typename T>
class VectorT : std::vector<T> {
    using vec = std::vector<T>;

public:
    using vec::at;
    using vec::emplace_back;
    using vec::push_back;

    template<typename Q = T,
             typename std::enable_if<!is_unique_ptr<Q>::value>::type* = nullptr>
    Q& operator [](std::size_t pos) { return vec::operator[](pos); }
    template<typename Q = T,
             typename std::enable_if<!is_unique_ptr<Q>::value>::type* = nullptr>
    Q const& operator [](std::size_t pos) const { return vec::operator[](pos); }

    template<typename Q = T,
             typename U = typename unique_ptr_type<Q>::type>
    U* operator [](std::size_t pos) { return vec::operator[](pos).get(); }
    template<typename Q = T,
             typename U = typename unique_ptr_type<Q>::type>
    U const* operator [](std::size_t pos) const { return vec::operator[](pos).get(); }
};

Online Demo

SFINAE is used to only enable the custom operator[] if T is std::unique_ptr<T>, and to only enable std::vector<T>::operator[] otherwise.

Comments