C++ 在容器、PTR和对象不可修改的情况下,如何传递唯一PTR的容器?

C++ 在容器、PTR和对象不可修改的情况下,如何传递唯一PTR的容器?,c++,stl,containers,const-correctness,C++,Stl,Containers,Const Correctness,我有一个容器向量,它具有某种类型的std::unique\u ptr。我想返回该容器,但也希望强制执行我不希望容器、指针或指向的对象是可修改的。我也不想对这个物体进行平行复制。我的别名类型类似于: using container_t = vector<std::unique_ptr<my_type_t>> 我认为这应该行得通,但我想知道是否有什么我没有看到的陷阱,或者是否有其他更好的方法 我还可以想象,这可能会导致最终生成中出现重复的二进制代码,但由于这些代码很可能是内

我有一个容器向量,它具有某种类型的std::unique\u ptr。我想返回该容器,但也希望强制执行我不希望容器、指针或指向的对象是可修改的。我也不想对这个物体进行平行复制。我的别名类型类似于:

using container_t = vector<std::unique_ptr<my_type_t>>
我认为这应该行得通,但我想知道是否有什么我没有看到的陷阱,或者是否有其他更好的方法

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

问题在于定义了std::unique\u ptr::operator*以返回非常量引用:

std::add_lvalue_reference<T>::type operator*() const
这将在get_容器返回常量引用时保护元素不被修改:

std::add_lvalue_reference<T>::type operator*() const
prog.cc:26:1:错误:成员函数“nonConst”的“this”参数已更改 “常数”类型 boost::ptr\u container\u detail::reversible\u ptr\u container>>, boost::heap\u clone\u allocator>::Ty_uu'aka'const X',但函数是 未标记常量a.get_容器[0]。非常量^~~~~~~~~~~~~~~~~~~~ prog.cc:9:9:注意:此处声明的“nonConst”无效nonConst{} ^生成1个错误


我不想包含boost和span,因为正如@Jens指出的,一个独特的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>
这是包装好的唯一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));
}

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

我不想重新解释强制转换,因为我不确定这是否是未定义的行为。您可能可以返回类似span的内容instead@Justin独特的ptr不是有问题吗?所以它必须是span,但这不会有帮助,因为unique_ptr::operator*返回一个非常量引用。@Jens这就是为什么我说的,类似于。它本身不可能是一个跨度,但返回的任何内容都可以使用相同的跨度概念
#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;
}
#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;
}
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>
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));
}