当 Type 为 unique_ptr 时,为 vector 添加 operator[]
Add operator[] for vector when Type is unique_ptr
假设我们有下面的向量 class,它已被缩短到最小值以展示问题。
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(); }
};
有什么方法可以检查 T 是否是 unique_ptr,如果是,则将运算符 [] 添加到 return 或 unique_ptr::element_type*。同时,正常的 operator[] 也应该可以工作。
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;
有什么方法可以实现这样的目标吗?
已编辑:
我要求这个的原因是我可以说我的容器
class MyVec: public VectorT<std::unique_ptr<SomeClass>>
但我也可以有一个
class MyVecView: public VectorT<SomeClass*>
两个 classes 将具有几乎相同的功能。所以我试图通过做
之类的事情来避免重复
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
....
}
}
那我当然可以
MyVec::doSomething(){doSomething(*this);}
MyVecView::doSomething(){doSomething(*this);}
这当然意味着 operator[]
必须适用于这两种情况
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(); }
};
SFINAE 用于仅在 T
为 std::unique_ptr<T>
时启用自定义 operator[]
,否则仅启用 std::vector<T>::operator[]
。
如果你坚持使用简单的指针,你可以这样写
template<class T> auto plain_ptr(T* p) { return p; }
template<class T> auto plain_ptr(std::unique_ptr<T>& p) { return p.get(); }
然后
tmp = plain_ptr(vec[i]); // tmp will be SomeClass*
或者你可以在本地安装 tmp:
template<typename T>
void doSomething(VectorT<T>& vec)
{
static_assert(std::is_same<T, SomeClass*>::value ||
std::is_same<T, std::unique_ptr<SomeClass>>::value,
"doSomething expects vectors containing SomeClass!");
for (std::size_t i = 0; i < vec.size(); ++i)
{
auto tmp = vec[i];
// ... (*tmp).foo or tmp->foo ...
}
// or perhaps even
for(auto&& tmp : vec)
{
// ... again (*tmp).foo or tmp->foo ...
}
}
这里的目标是只有一个 operator[]
。超过一个 operator[]
的技术违反了 DRY(不要重复自己),并且很难避免有一个模板方法,如果实例化,其主体将无法编译(在严格阅读标准的情况下,可能会导致在你的代码中格式不正确)。
所以我要做的是像这样对 "turn something into a pointer" 建模:
namespace details {
template<class T>
struct plain_ptr_t;
//specialzation for T*
template<class T>
struct plain_ptr_t<T*> {
T* operator()(T* t)const{return t;}
};
//specialzation for std::unique_ptr
template<class T, class D>
struct plain_ptr_t<std::unique_ptr<T,D>> {
T* operator()(std::unique_ptr<T>const& t)const{return t.get();}
};
//specialzation for std::shared_ptr
template<class T>
struct plain_ptr_t<std::shared_ptr<T>> {
T* operator()(std::shared_ptr<T>const& t)const{return t.get();}
};
}
struct plain_ptr {
template<class T>
typename std::result_of< details::plain_ptr_t<T>( T const& ) >::type
operator()( T const& t ) const {
return details::plain_ptr_t<T>{}( t );
}
};
现在 plain_ptr
是一个仿函数,它将智能指针映射到普通指针,并将指针映射到指针。
它拒绝不是指针的东西。如果愿意,您可以将其更改为只让它们通过,但这需要一点小心。
然后我们使用它们来改进您的 operator[]
:
typename std::result_of< plain_ptr(typename vec::value_type const&)>::type
operator[](size_t pos) const {
return plain_ptr{}(at(pos));
}
注意它不再是 template
。
假设我们有下面的向量 class,它已被缩短到最小值以展示问题。
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(); }
};
有什么方法可以检查 T 是否是 unique_ptr,如果是,则将运算符 [] 添加到 return 或 unique_ptr::element_type*。同时,正常的 operator[] 也应该可以工作。
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;
有什么方法可以实现这样的目标吗?
已编辑:
我要求这个的原因是我可以说我的容器
class MyVec: public VectorT<std::unique_ptr<SomeClass>>
但我也可以有一个
class MyVecView: public VectorT<SomeClass*>
两个 classes 将具有几乎相同的功能。所以我试图通过做
之类的事情来避免重复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
....
}
}
那我当然可以
MyVec::doSomething(){doSomething(*this);}
MyVecView::doSomething(){doSomething(*this);}
这当然意味着 operator[]
必须适用于这两种情况
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(); }
};
SFINAE 用于仅在 T
为 std::unique_ptr<T>
时启用自定义 operator[]
,否则仅启用 std::vector<T>::operator[]
。
如果你坚持使用简单的指针,你可以这样写
template<class T> auto plain_ptr(T* p) { return p; }
template<class T> auto plain_ptr(std::unique_ptr<T>& p) { return p.get(); }
然后
tmp = plain_ptr(vec[i]); // tmp will be SomeClass*
或者你可以在本地安装 tmp:
template<typename T>
void doSomething(VectorT<T>& vec)
{
static_assert(std::is_same<T, SomeClass*>::value ||
std::is_same<T, std::unique_ptr<SomeClass>>::value,
"doSomething expects vectors containing SomeClass!");
for (std::size_t i = 0; i < vec.size(); ++i)
{
auto tmp = vec[i];
// ... (*tmp).foo or tmp->foo ...
}
// or perhaps even
for(auto&& tmp : vec)
{
// ... again (*tmp).foo or tmp->foo ...
}
}
这里的目标是只有一个 operator[]
。超过一个 operator[]
的技术违反了 DRY(不要重复自己),并且很难避免有一个模板方法,如果实例化,其主体将无法编译(在严格阅读标准的情况下,可能会导致在你的代码中格式不正确)。
所以我要做的是像这样对 "turn something into a pointer" 建模:
namespace details {
template<class T>
struct plain_ptr_t;
//specialzation for T*
template<class T>
struct plain_ptr_t<T*> {
T* operator()(T* t)const{return t;}
};
//specialzation for std::unique_ptr
template<class T, class D>
struct plain_ptr_t<std::unique_ptr<T,D>> {
T* operator()(std::unique_ptr<T>const& t)const{return t.get();}
};
//specialzation for std::shared_ptr
template<class T>
struct plain_ptr_t<std::shared_ptr<T>> {
T* operator()(std::shared_ptr<T>const& t)const{return t.get();}
};
}
struct plain_ptr {
template<class T>
typename std::result_of< details::plain_ptr_t<T>( T const& ) >::type
operator()( T const& t ) const {
return details::plain_ptr_t<T>{}( t );
}
};
现在 plain_ptr
是一个仿函数,它将智能指针映射到普通指针,并将指针映射到指针。
它拒绝不是指针的东西。如果愿意,您可以将其更改为只让它们通过,但这需要一点小心。
然后我们使用它们来改进您的 operator[]
:
typename std::result_of< plain_ptr(typename vec::value_type const&)>::type
operator[](size_t pos) const {
return plain_ptr{}(at(pos));
}
注意它不再是 template
。