如何传递容器、ptr 和对象不可修改的unique_ptrs容器?
How to pass a container of unique_ptrs where container, ptrs and objects are not modifiable?
我有一个容器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>>
并为我的getter做一个reinterpret_cast
:
const_container_t& encompassing_type::get_container() const
{
return reinterpret_cast<const_container_t&>(m_container);
}
我认为这应该有效,但我想知道是否有任何我没有看到的陷阱,或者是否有其他更好的方法来做到这一点。
我还认为这可能会导致最终版本中出现重复的二进制代码,但由于这些代码很可能是内联的,这应该不是问题。
"问题"是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 );
}
Justin 建议使用类似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;
}
对于优化编译器,这应该是相当有效的。
您也可以从std::vector<std::unique_ptr<my_type_t>>
切换到boost::ptr_vector<my_type_t>
.它还假定指针存储的元素的所有权,但它返回一个const_reference
inoperator[] const
,以便无法修改对象。
#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:错误:"this"参数到成员函数"nonConst"有 键入 'const boost::p tr_container_detail::reversible_ptr_container>>, boost::heap_clone_allocator>::Ty_'(又名"const X"),但函数是 未标记 const a.get_container()[0].nonConst();^~~~~~~~~~~~~~~~~~~~ prog.cc:9:9: 注意: 'nonConst' 在这里声明 void nonConst() {} ^ 生成 1 个错误。
我不想包含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 clashes 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++20std::remove_cvref_t
的实现:
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
这是包装的唯一ptr:
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));
}
感谢您的帮助,并提醒我这只是一个传播问题。
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 为什么 std::unique 不调用 std::sort?
- 独立读取-修改-写入顺序
- 当系统的卷被修改时,如何修改WASAPI环回捕获卷
- 修改函数中的指针(将另一个指针作为参数传递)
- 为什么我可以通过引用修改常量返回
- 对于结构,表达式必须是可修改的ivalue
- QML:修改在不同QML文件(而非main.QML)中定义的子对象的属性
- 为什么不能修改对象中的值?另外,我如何改进此链表?
- 修改创建帐户程序
- 我应该如何修改此代码以使用给定字符串中的字母打印菱形图案
- 如何从子成员函数修改父公共成员变量
- 修改 VS Code 中的默认C++代码段
- 为什么在我的函数类型后使用引用运算符 (&) 允许我修改它返回的值?
- 如何使用递归打印修改后的星号三角形图案
- 已修改的LinkedList未在文本文件本身中更新
- C++RapidXml-使用first_node()遍历以修改XML文件中节点的值
- 为什么可以修改数组 b?
- 将 C++ 类与 Rcpp 一起使用,从 C 或 R 修改它
- 修改的Fibbonaci C++得到一个大的负数