我想将unique_ptr
传递给辅助函数,我想确保辅助函数既不修改指针也不修改指针对象。没有unique_ptr
,解决方案是
void takePtr(AClass const * const aPtr) {
// Do something with *aPtr.
// We cannot change aPtr, not *aPtr.
}
(嗯,从技术上讲,AClass const * aPtr
就够了。)我可以用
AClass * aPtr2 = new AClass(3);
takePtr(aPtr2);
我想改用unique_ptr
,但无法弄清楚如何写这个。我试过了
void takeUniquePtr(unique_ptr<AClass const> const & aPtr) {
// Access *aPtr, but should not be able to change aPtr, or *aPtr.
}
当我用
打电话时unique_ptr<AClass> aPtr(new AClass(3));
takeUniquePtr(aPtr);
它无法编译。我看到的错误是
testcpp/hello_world.cpp:141:21: error: invalid user-defined conversion from ‘std::unique_ptr<AClass>’ to ‘const std::unique_ptr<const AClass>&’ [-fpermissive]
unique_ptr<AClass>
到unique_ptr<AClass const>
的转换不应该是自动的吗?我在这里错过了什么?
顺便说一下,如果我在函数定义中将unique_ptr<AClass const> const & aPtr
更改为unique_ptr<AClass> const & aPtr
,它会编译,但是我可以调用像aPtr->changeAClass()
这样的函数,我不想让它。
答案 0 :(得分:25)
智能指针用于管理所有权和生命周期,它们允许我们(除其他事项外)安全地转移代码各部分的所有权。
当你将const unique_ptr<T>&
传递给一个函数时(与T
是否const
无关),它实际意味着该函数承诺永远不会修改{{1}本身(但如果unique_ptr
不是T
),它仍然可以修改指向对象,即。没有任何可能的所有权转让。您只是使用const
作为裸指针周围的无用包装。
所以,正如@MarshallClow在评论中建议的那样,你应该摆脱包装并传递裸指针或直接引用。这很酷的是你的代码现在语义清晰(你的功能的签名清楚地表明它并没有弄乱所有权,这是不立即显而易见unique_ptr
),它解决了你的问题&#34;问题在同一时间!
即:
const unique_ptr<...>&
修改以解决您的第二个问题&#34; 为什么编译器不允许我......将void someFunction(const AClass* p) { ... }
std::unique_ptr<AClass> ptr(new AClass());
someFunction(ptr.get());
强制转换为unique_ptr<A>
? &#34;
实际上,您可以移动 unique_ptr<A const>
到unique_ptr<A>
:
unique_ptr<A const>
但正如您所知,这意味着将所有权从std::unique_ptr<A> p(new A());
std::unique_ptr<const A> q(std::move(p));
转移到p
。
您的代码存在的问题是您将(引用)q
传递给函数。由于与unique_ptr<const A>
存在类型差异,为了使其工作,编译器需要实例化临时。但除非您使用unique_ptr<A>
手动转移所有权,否则编译器会尝试复制您的std::move
,但由于unique_ptr
明确禁止,因此无法执行此操作它
请注意如果移动unique_ptr
:
unique_ptr
编译器现在能够构建一个临时void test(const std::unique_ptr<const int>& p) { ... }
std::unique_ptr<int> p(new int(3));
test(std::move(p));
并移动原始unique_ptr<const A>
而不会打破您的期望(因为现在很清楚您想移动,而不是复制)。
因此,问题的根源是unique_ptr<A>
只有移动语义而不是复制语义,但是您需要复制语义来创建临时和然后保持所有权。鸡蛋和鸡肉,unique_ptr
就是这样设计的。
如果您现在考虑unique_ptr
具有复制语义,问题也会消失。
shared_ptr
原因是编译器现在能够创建一个临时的void test(const std::shared_ptr<const int>& p) { ... }
std::shared_ptr<int> p(new int(3));
test(p);
//^^^^^ Works!
副本(从std::shared_ptr<const int>
自动转换)并将该临时绑定到std::shared_ptr<int>
参考
我想这或多或少涵盖了它,即使我的解释缺乏标准术语,也许不是应该如此清晰。 :)
答案 1 :(得分:2)
关于const智能指针的这个老问题。 以上答案忽略了简单的模板解决方案。
template<class T>
void foo(const unique_ptr<T>& ptr) {
// do something with ptr
}
此解决方案允许将unique_ptr的所有可能选项发送到foo:
const unique_ptr<const int>
unique_ptr<const int>
const unique_ptr<int>
unique_ptr<int>
如果您特别想因上述某些原因而避免使用上述3和4,请将const添加到T:
template<class T>
void foo(const unique_ptr<const T>& ptr) {
// do something with ptr
}
如果您可以或不可以改变指向值的情况获得不同的行为,您可以重载“选项a”和“选项b”。
如果您不想对此功能中的指向值进行任何更改(从不!我们获得的参数类型!) - 不要超载。
使用“选项b”,编译器将不允许更改我们指向的值。完成工作!
如果你想支持所有4种情况,即“选项a”,该功能仍然可能“意外地”改变我们指向的值,例如
template<class T>
void foo(const unique_ptr<T>& ptr) {
*ptr = 3;
}
然而,如果至少有一个调用者的T实际上是const,那么这应该不是问题,在这种情况下编译器不会喜欢它并帮助你解决问题。
您可以在单元测试中添加这样的调用者,例如:
foo(make_unique<const int>(7)); // if this line doesn't compile someone
// is changing value inside foo which is
// not allowed!
// do not delete the test, fix foo!