如何在容器,ptrs和对象不可修改的情况下传递unique_ptrs容器?
Posted
技术标签:
【中文标题】如何在容器,ptrs和对象不可修改的情况下传递unique_ptrs容器?【英文标题】:How to pass a container of unique_ptrs where container, ptrs and objects are not modifiable? 【发布时间】:2018-03-19 21:01:07 【问题描述】:我有一个容器vector
,其中包含某种类型的std::unique_ptr
。我想返回那个容器,但也想强制我不希望容器、指针或指向的对象是可修改的。我也不想制作这个对象的一些并行副本。我的别名类型类似于:
using container_t = vector<std::unique_ptr<my_type_t>>
所以我想我可以像这样制作另一个别名:
using const_container_t = const vector<std::unique_ptr<const my_type_t>>
并为我的吸气剂做一个reinterpret_cast
:
const_container_t& encompassing_type::get_container() const
return reinterpret_cast<const_container_t&>(m_container);
我认为这应该可行,但我想知道是否有任何我没有看到的问题,或者是否有其他更好的方法。
我还认为这可能会导致最终构建中出现重复的二进制代码,但由于这些很可能是内联的,所以这应该不是问题。
【问题讨论】:
我会远离reinterpret_cast
,因为我不确定这是否是未定义的行为。
您也许可以返回 span<const my_type_t>
之类的东西
@Justin 不是unique_ptr
的问题吗?所以它必须是span<unique_ptr<my_type_t>>
,但这无济于事,因为unique_ptr::operator*
返回一个非常量引用。
@Jens 这就是我说“类似”的原因。它本身不可能是 span
,但无论返回什么都可以使用 span
的相同想法
【参考方案1】:
“问题”是std::unique_ptr::operator*
被定义为返回一个非常量引用:
std::add_lvalue_reference<T>::type operator*() const
由于它是一个内部类,您可以使用普通指针并显式管理生命周期,允许您执行类似的操作
span<my_type_t const> encompassing_type::get_container() const
return span( m_container );
贾斯汀建议使用span<const my_type_t>
之类的东西来实现指向向量的常量指针的视图。你可以例如使用 Boost.Range 执行此操作并返回一系列 const 指针:
#include <boost/range.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace boost::adaptors;
class X
public:
void nonConst()
void constF() const
;
class A
std::vector<std::unique_ptr<X>> v;
public:
A() : v(10)
auto get_container()
return v | transformed( [](std::unique_ptr<X> const& x) -> X const* return x.get(););
;
int main()
A a;
auto const& v = a.get_container();
a.get_container()[0]->constF();
a.get_container()[0]->nonConst();
return 0;
This should be fairly efficient with an optimizing compiler.
您也可以从std::vector<std::unique_ptr<my_type_t>>
切换到boost::ptr_vector<my_type_t>
。它还假定指针存储的元素的所有权,但它在operator[] const
中返回const_reference
,这样对象就不能被修改。
#include <boost/ptr_container/ptr_vector.hpp>
class X
public:
void nonConst()
;
class A
boost::ptr_vector<X> v;
public:
boost::ptr_vector<X> const& get_container() const
return v;
;
int main()
A a;
auto const& v = a.get_container();
a.get_container()[0].nonConst();
return 0;
这将保护元素在get_container()
返回一个常量引用时不被修改:
prog.cc:26:1: 错误:成员函数“nonConst”的“this”参数有 输入'常量 boost::ptr_container_detail::reversible_ptr_container > >, boost::heap_clone_allocator>::Ty_'(又名'const X'),但函数是 未标记 const a.get_container()[0].nonConst(); ^~~~~~~~~~~~~~~~~~~~ prog.cc:9:9:注意:此处声明的 'nonConst' 无效 nonConst() ^ 产生 1 个错误。
【讨论】:
【参考方案2】:我不想包含 boost 并且 span
不起作用,因为正如 @Jens 指出的那样,unique_ptr
不会传播 cv 限定符。此外,即使我确实包含了 boost,我也无法获得向量中每个项目的实际对象引用,我需要通过它来比较对象与容器中其他对象的相对位置。
所以我选择在 std::unique_ptr
上编写一个包装器,它将传播 cv 限定符。
以下是我的enable_if.h
文件的摘录,我将其用于比较运算符以限制我必须编写它们的次数:
namespace detail
// Reason to use an enum class rather than just an int is so as to ensure
// there will not be any ***es resulting in an ambiguous overload.
enum class enabler
enabled
;
#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<__VA_ARGS__, detail::enabler>
这是我对c++20的std::remove_cvref_t
的实现:
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
这是包装后的唯一指针:
template <typename T, typename D = std::default_delete<T>>
class unique_ptr_propagate_cv;
namespace detail
template <typename T, typename D>
std::unique_ptr<T, D> const& get_underlying_unique_ptr(unique_ptr_propagate_cv<T, D> const& object)
return object.ptr;
template <typename T, typename D>
class unique_ptr_propagate_cv
template <typename T_, typename D_>
friend std::unique_ptr<T_, D_> const& detail::get_underlying_unique_ptr<T_, D_>(unique_ptr_propagate_cv<T_, D_> const&);
using base = std::unique_ptr<T, D>;
base ptr;
public:
template <typename...Ts>
unique_ptr_propagate_cv(Ts&&...args) noexcept : ptr(std::forward<Ts>(args)...)
using element_type = typename base::element_type;
using deleter_type = typename base::deleter_type;
using pointer = element_type *;
using pointer_const = element_type const *;
using pointer_volatile = element_type volatile *;
using pointer_const_volatile = element_type const volatile *;
using reference = element_type &;
using reference_const = element_type const &;
using reference_volatile = element_type volatile &;
using reference_const_volatile = element_type const volatile &;
pointer get() noexcept return ptr.get();
pointer_const get() const noexcept return ptr.get();
pointer_volatile get() volatile noexcept return ptr.get();
pointer_const_volatile get() const volatile noexcept return ptr.get();
pointer operator->() noexcept return ptr.get();
pointer_const operator->() const noexcept return ptr.get();
pointer_volatile operator->() volatile noexcept return ptr.get();
pointer_const_volatile operator->() const volatile noexcept return ptr.get();
reference operator[](size_t index) noexcept return ptr.operator[](index);
reference_const operator[](size_t index) const noexcept return ptr.operator[](index);
reference_volatile operator[](size_t index) volatile noexcept return ptr.operator[](index);
reference_const_volatile operator[](size_t index) const volatile noexcept return ptr.operator[](index);
reference operator*() noexcept return ptr.operator*();
reference_const operator*() const noexcept return ptr.operator*();
reference_volatile operator*() volatile noexcept return ptr.operator*();
reference_const_volatile operator*() const volatile noexcept return ptr.operator*();
template <typename T_>
unique_ptr_propagate_cv& operator=(T_&& rhs)
return static_cast<unique_ptr_propagate_cv&>(ptr.operator=(std::forward<T_>(rhs)));
decltype(auto) get_deleter() const noexcept return ptr.get_deleter();
operator bool() const noexcept return ptr.operator bool();
decltype(auto) reset(pointer ptr = pointer()) noexcept get_base_nonconst().reset(ptr);
decltype(auto) release() noexcept return get_base_nonconst().release();
;
template <typename T>
struct is_unique_ptr_propagate_cv : std::false_type ;
template <typename T, typename D>
struct is_unique_ptr_propagate_cv<unique_ptr_propagate_cv<T, D>> : std::true_type ;
namespace detail
inline nullptr_t const& get_underlying_unique_ptr(nullptr_t const& object)
return object;
template <typename T, typename D>
std::unique_ptr<T, D> const& get_underlying_unique_ptr(std::unique_ptr<T, D> const& object)
return object;
template <typename L, typename R
, ENABLE_IF(
is_unique_ptr_propagate_cv<remove_cvref_t<L>>::value
|| is_unique_ptr_propagate_cv<remove_cvref_t<R>>::value
)
>
bool operator==(L&& lhs, R&& rhs) noexcept
return detail::get_underlying_unique_ptr(std::forward<L>(lhs))
== detail::get_underlying_unique_ptr(std::forward<R>(rhs));
template <typename L, typename R
, ENABLE_IF(
is_unique_ptr_propagate_cv<remove_cvref_t<L>>::value
|| is_unique_ptr_propagate_cv<remove_cvref_t<R>>::value
)
>
auto operator!=(L&& lhs, R&& rhs) noexcept
return detail::get_underlying_unique_ptr(std::forward<L>(lhs))
!= detail::get_underlying_unique_ptr(std::forward<R>(rhs));
template <typename L, typename R
, ENABLE_IF(
is_unique_ptr_propagate_cv<remove_cvref_t<L>>::value
|| is_unique_ptr_propagate_cv<remove_cvref_t<R>>::value
)
>
bool operator<=(L&& lhs, R&& rhs) noexcept
return detail::get_underlying_unique_ptr(std::forward<L>(lhs))
<= detail::get_underlying_unique_ptr(std::forward<R>(rhs));
template <typename L, typename R
, ENABLE_IF(
is_unique_ptr_propagate_cv<remove_cvref_t<L>>::value
|| is_unique_ptr_propagate_cv<remove_cvref_t<R>>::value
)
>
bool operator>=(L&& lhs, R&& rhs) noexcept
return detail::get_underlying_unique_ptr(std::forward<L>(lhs))
>= detail::get_underlying_unique_ptr(std::forward<R>(rhs));
template <typename L, typename R
, ENABLE_IF(
is_unique_ptr_propagate_cv<remove_cvref_t<L>>::value
|| is_unique_ptr_propagate_cv<remove_cvref_t<R>>::value
)
>
bool operator<(L&& lhs, R&& rhs) noexcept
return detail::get_underlying_unique_ptr(std::forward<L>(lhs))
< detail::get_underlying_unique_ptr(std::forward<R>(rhs));
template <typename L, typename R
, ENABLE_IF(
is_unique_ptr_propagate_cv<remove_cvref_t<L>>::value
|| is_unique_ptr_propagate_cv<remove_cvref_t<R>>::value
)
>
bool operator >(L&& lhs, R&& rhs) noexcept
return detail::get_underlying_unique_ptr(std::forward<L>(lhs))
> detail::get_underlying_unique_ptr(std::forward<R>(rhs));
感谢您的帮助并提醒我这只是传播问题。
【讨论】:
以上是关于如何在容器,ptrs和对象不可修改的情况下传递unique_ptrs容器?的主要内容,如果未能解决你的问题,请参考以下文章
TypeError:“sqlite3.Cursor”对象不可下标。如何在没有for循环的情况下打印sql选择数据[重复]