代码之家  ›  专栏  ›  技术社区  ›  Adrian

在容器、PTR和对象不可修改的情况下,如何传递unique\u ptr的容器?

  •  0
  • Adrian  · 技术社区  · 7 年前

    我有一个容器 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 对于我的getter:

    const_container_t& encompassing_type::get_container() const
    {
      return reinterpret_cast<const_container_t&>(m_container);
    }
    

    我认为这应该行得通,但我想知道是否有什么我没有看到的缺陷,或者是否有其他更好的方法来做到这一点。

    我还可以想象,这可能会导致最终构建中出现重复的二进制代码,但由于这些代码很可能是内联的,所以这不应该是一个问题。

    2 回复  |  直到 7 年前
        1
  •  1
  •   Jens    7 年前

    “问题”是 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执行此操作。范围并返回常量指针的范围:

    #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> 。它还假定指针存储的元素的所有权,但它返回 const_reference 在里面 operator[] 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() 返回常量引用:

    程序。抄送:26:1:错误:成员函数“noncost”的“this”参数 “const”类型 boost::ptr\u container\u detail::reversible\u ptr\u container>>, boost::heap\u clone\u分配器>::Ty_uu'(又名“const X”),但函数为 未标记const a.get\u container()[0]。非标准()^~~~~~~~~~~~~~~~~~~~ 程序。抄送:9:9:注意:“noncost”在此处声明void noncost(){} ^生成1个错误。

        2
  •  0
  •   Adrian    7 年前

    我不想包括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++20的实现 std::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));
    }
    

    感谢您的帮助,并提醒我这只是一个传播问题。