C++ 使用std::unique\u ptr/std::shared\u ptr常量正确组合

C++ 使用std::unique\u ptr/std::shared\u ptr常量正确组合,c++,pointers,c++14,constants,const-correctness,C++,Pointers,C++14,Constants,Const Correctness,目前我想知道如何正确地使用std::unique_ptr作为常量正确性的成员变量 以下示例允许更改my_foo拥有的内容,尽管它是常量: #include <iostream> #include <memory> struct foo { foo() : value_ptr_(std::make_unique<int>(3)) {} void increment() const { ++(*value_ptr_); }

目前我想知道如何正确地使用
std::unique_ptr
作为常量正确性的成员变量

以下示例允许更改my_foo拥有的内容,尽管它是常量:

#include <iostream>
#include <memory>

struct foo {
    foo() : value_ptr_(std::make_unique<int>(3)) {}
    void increment() const {
        ++(*value_ptr_);
    }
    int get_value() const {
        return *value_ptr_;
    }
    std::unique_ptr<int> value_ptr_;
};

int main() {
    const foo my_foo;
    std::cout << my_foo.get_value() << std::endl;
    my_foo.increment(); // But my_foo is const!
    std::cout << my_foo.get_value() << std::endl;
}
在这个最小的例子中,有一个指向int的指针当然不是很有意义,但在实际代码中,
unique\u ptr
可能包含一个指向某个多态对象的基类的指针,即我们不能简单地按值存储的对象


那么,如何更好地处理这种情况呢?

我这样做的方法是提供一个内部协议,以提供对底层实现的正确约束引用的访问

大概是这样的:

struct foo {
    // standard (in your codebase) protocol to express the impl base class
    using underlying_impl = int;

    // standard protocol to express ownership semantics
    using implementation_handle = std::unique_ptr<underlying_impl>;

    // construction via private 'construct' protocol    
    foo() : value_ptr_(construct(3)) {}

    // all internal access to the implementation via a the protocol
    // of get_impl()
    auto operator++() -> foo&
    {
        // not-const - compiles fine
        ++get_impl();
        return *this;
    }

    void increment() const {
// now won't compile - get_impl() propagates const correctly
//        ++get_impl();
    }

private:

    static auto construct(int val) -> implementation_handle
    {
        return std::make_unique<underlying_impl>(val);
    }

    // two versions of get_impl() - const and mutable
    auto get_impl() const -> underlying_impl const&
    {
        return *value_ptr_;
    }

    auto get_impl() -> underlying_impl&
    {
        return *value_ptr_;
    }

    // actual storage of the implementation handle
    implementation_handle value_ptr_;
};
structfoo{
//用于表示impl基类的标准(在代码库中)协议
使用基础的_impl=int;
//表示所有权语义的标准协议
使用实现\u handle=std::unique\u ptr;
//通过专用“构造”协议构造
foo():value_ptr_(构造(3)){}
//所有通过协议的内部访问实现
//关于get_impl()的
自动运算符++()->foo&
{
//不是常量-编译很好
++get_impl();
归还*这个;
}
无效增量()常量{
//现在无法编译-get_impl()正确传播常量
//++get_impl();
}
私人:
静态自动构造(int-val)->实现\u句柄
{
返回标准::使_唯一(val);
}
//get_impl()的两个版本-const和mutable
自动获取\u impl()常量->基础\u impl常量&
{
返回*值\u ptr;
}
自动获取\u impl()->基础\u impl&
{
返回*值\u ptr;
}
//实现句柄的实际存储
实现(处理值)(ptr);;
};

您可以继承
std::unique\u ptr
并仅覆盖3个(4个用于
unique\u ptr
)方法,提供常量/非常量重载:

template <typename T>
struct propagating_unique_ptr : std::unique_ptr<T> {
    using unique_ptr<T>::unique_ptr;
    using unique_ptr<T>::operator =;

    const T *get() const noexcept {
        return unique_ptr<T>::get();
    }
    T *get() noexcept {
        return unique_ptr<T>::get();
    }

    const T &operator *() const noexcept {
        return unique_ptr<T>::operator *();
    }
    T &operator *() noexcept {
        return unique_ptr<T>::operator *();
    }

    const T *operator -> () const noexcept {
        return unique_ptr<T>::get();
    }
    T *operator -> () noexcept {
        return unique_ptr<T>::get();
    }
}; 
模板
结构传播_unique_ptr:std::unique_ptr{
使用unique_ptr::unique_ptr;
使用唯一的_ptr::operator=;
常量T*get()常量noexcept{
返回唯一的_ptr::get();
}
T*get()无异常{
返回唯一的_ptr::get();
}
常量T&运算符*()常量noexcept{
返回唯一的_ptr::运算符*();
}
T运算符*()不例外(&O){
返回唯一的_ptr::运算符*();
}
常量T*运算符->()常量noexcept{
返回唯一的_ptr::get();
}
T*运算符->()无例外{
返回唯一的_ptr::get();
}
}; 

我不知道为什么允许对
const foo
对象调用
increment
?基于名称,在这种情况下不允许调用它,我想象的情况与使用原始指针的情况类似。某种包装类。似乎就是它的用途。但是我没有足够的知识来写一个答案。@我的问题是,我被允许首先用一个
const
说明符来写
increment
。@NickyC,是的,看起来
std::experiative::propagate\u const
正是我要找的。看来我得等很长时间才能使用它谢谢。您的解决方案工作得很好,我喜欢它,因为模式被抽象掉了,实际的类
foo
不能做太多修改。:)
template <typename T>
struct propagating_unique_ptr : std::unique_ptr<T> {
    using unique_ptr<T>::unique_ptr;
    using unique_ptr<T>::operator =;

    const T *get() const noexcept {
        return unique_ptr<T>::get();
    }
    T *get() noexcept {
        return unique_ptr<T>::get();
    }

    const T &operator *() const noexcept {
        return unique_ptr<T>::operator *();
    }
    T &operator *() noexcept {
        return unique_ptr<T>::operator *();
    }

    const T *operator -> () const noexcept {
        return unique_ptr<T>::get();
    }
    T *operator -> () noexcept {
        return unique_ptr<T>::get();
    }
};