简体   繁体   English

是否支持从unique_ptr到原始指针的隐式转换?

[英]Is implicit conversion from unique_ptr to raw pointer supported?

I was reading Effective C++ 3rd Edition. 我正在阅读有效的C ++第三版。 In page 70, the author says: 在第70页中,作者说:

Like virtually all smart pointer classes, tr1::shared_ptr and auto_ptr also overload the pointer dereferencing operators ( operator-> and operator* ), and this allows implicit conversion to the underlying raw pointers (...) 与几乎所有智能指针类一样, tr1::shared_ptrauto_ptr也会使指针解引用运算符( operator->operator* )过载,这允许隐式转换为基础原始指针(...)

He then shows an example with shared_ptr (which was part of tr1 at the time) featuring implicit conversion based on a class named Investment : 然后,他展示了shared_ptr (当时是tr1一部分)的示例,该示例基于名为Investment的类进行隐式转换:

shared_ptr<Investment> pi1();
bool taxable1 = !(pi1->isTaxFree());
                    ^implicit conversion

shared_ptr<Investment> pi2();
bool taxable2 = !((*pi2).isTaxFree());
                    ^implicit conversion

Well, I have since then written a few test cases with unique_ptr and they hold up. 好吧,从那时起,我就用unique_ptr编写了一些测试用例,它们得到了unique_ptr

I also found out about unique_ptr supporting arrays and shared_ptr also going to (see note). 我还发现了支持数组的unique_ptrshared_ptr也将要使用 (请参阅注释)。 However, in my testing, implicit conversion does not seem to work for smart pointers around arrays. 但是,在我的测试中,隐式转换似乎不适用于数组周围的智能指针。

Example: I wanted this to be valid... 示例:我希望这是有效的...

unique_ptr<int[]> test(new int[1]);

(*test)[0] = 5;

but it is not, according to my compiler (Visual C++ 2015 Update 3). 但根据我的编译器(Visual C ++ 2015 Update 3),它不是。

Then, from a little research, I found some evidence suggesting that implicit conversion isn't supported at all... like this one for instance: https://herbsutter.com/2012/06/21/reader-qa-why-dont-modern-smart-pointers-implicitly-convert-to . 然后,通过一项小小的研究,我发现了一些证据表明根本不支持隐式转换...例如: https : //herbsutter.com/2012/06/21/reader-qa-why-不要将现代智能指针隐式转换为

At this point I am in doubt. 在这一点上,我有疑问。 Is it supported (by the Standard) , or is it not? (受标准支持还是不支持?


Note: The book might be a bit outdated on this topic, since the author also says on page 65 that "there is nothing like auto_ptr or tr1::shared_ptr for dinamically allocated arrays, not even in TR1". 注意:这本书在这个主题上可能有点过时,因为作者在第65页上还说过:“对于动态分配的数组,没有什么像auto_ptrtr1::shared_ptr一样,甚至在TR1中也没有。”

Well, here's the thing. 好吧,这就是事情。 There is no implicit conversion to the underlying pointer, you have to call a specific get member function (it's a theme in the standard library, think std::string::c_str ). 没有对基础指针的隐式转换,您必须调用特定的get成员函数(这是标准库中的主题,请考虑std::string::c_str )。

But that's a good thing! 但这是一件好事! Implicitly converting the pointer can break the guarantees of unique_ptr . 隐式转换指针可能会破坏unique_ptr的保证。 Consider the following: 考虑以下:

std::unique_ptr<int> p1(new int);
std::unique_ptr<int> p2(p1);

In the above code, the compiler can try to pass p1 s pointee to p2 ! 在上面的代码中,编译器可以尝试将p1的pointee传递给p2 (It won't since this call will be ambiguous anyway, but assume it wasn't). (因为此调用无论如何都不会模棱两可,但是假设不是)。 They will both call delete on it! 他们都将要求delete它!

But we still want to use the smart pointer as if it was a raw one. 但是我们仍然希望使用智能指针,就像它是原始指针一样。 Hence all the operators are overloaded. 因此,所有操作员都超负荷了。


Now let's consider your code: 现在,让我们考虑一下您的代码:

(*test)[0] = 5;

It calls unique_ptr::operator* which produces an int& 1 . 它调用unique_ptr::operator* ,它生成一个int& 1 Then you try use the subscript operator on it. 然后尝试在其上使用下标运算符。 That's your error. 那是你的错误
If you have a std::unique_ptr<int[]> than just use the operator[] overload that the handle provides: 如果您拥有std::unique_ptr<int[]>而不仅仅是使用该句柄提供的operator[]重载:

test[0] = 5;

1 As David Scarlett pointed out, it shouldn't even compile. 1正如David Scarlett指出的那样,它甚至不应该编译。 The array version isn't supposed to have this operator. 数组版本不应该具有此运算符。

As StoryTeller indicates, implicit conversions would ruin the show, but I'd like to suggest another way of thinking about this: 正如StoryTeller所指出的那样,隐式转换会破坏演出,但我想提出另一种思考方式:

Smart pointers like unique_ptr and shared_ptr try to hide the underlying raw pointer because they try to maintain a certain kind of ownership semantics over it. unique_ptrshared_ptr这样的智能指针试图隐藏底层的原始指针,因为它们试图在其上保留某种所有权语义。 If you were to freely obtain that pointer and pass it around, you could easily violate those semantics. 如果要自由获取该指针并将其传递,则很容易违反这些语义。 They still provide a way to access it ( get ), since they couldn't stop you completely even if they wanted to (after all you can just follow the smart pointer and get the address of the pointee). 他们仍然提供了一种访问它的方法( get ),因为即使他们愿意也无法完全阻止您(毕竟,您可以跟随智能指针并获取指针的地址)。 But they still want to put a barrier to make sure you don't access it accidentally. 但是他们仍然想设置障碍,以确保您不会意外访问它。

All is not lost though! 一切都不会丢失! You can still gain that syntactic convenience by defining a new kind of smart pointer with very weak semantics, such that it can safely be implicitly constructed from most other smart pointers. 您仍然可以通过定义一种语义非常弱的新型智能指针来获得这种语法上的便利,以便可以从大多数其他智能指针中隐式地构造该智能指针。 Consider: 考虑:

// ipiwdostbtetci_ptr stands for : 
// I promise I wont delete or store this beyond the expression that created it ptr
template<class T>
struct ipiwdostbtetci_ptr {
    T * _ptr;
    T & operator*() {return *_ptr;}
    T * operator->(){return _ptr;}
    ipiwdostbtetci_ptr(T * raw): _ptr{raw} {}
    ipiwdostbtetci_ptr(const std::unique_ptr<T> & unq): _ptr{unq.get()} {}
    ipiwdostbtetci_ptr(const std::shared_ptr<T> & shr): _ptr{shr.get()} {}
};

So, what's the point of this satirically named smart pointer? 那么,这个讽刺命名的智能指针有什么意义呢? It's just a kind of pointer that's verbally given a contract that the user will never keep it or a copy of it alive beyond the expression that created it and the user will also never attempt to delete it. 这只是一种指针,它在口头上赋予了用户永远不会保留它或它的副本超出创建它的表达式的生命,并且用户也永远不会尝试删除它的指针。 With these constraints followed by the user (without the compiler checking it), it's completely safe to implicitly convert many smart pointers as well as any raw pointer. 在用户遵循这些约束的情况下(无需编译器对其进行检查),隐式转换许多智能指针以及任何原始指针都是完全安全的。

Now you can implement functions that expect a ipiwdostbtetci_ptr (with the assumption that they'll honor the semantics), and conveniently call them: 现在,您可以实现期望具有ipiwdostbtetci_ptr函数(假设它们将ipiwdostbtetci_ptr语义),并方便地调用它们:

void f(ipiwdostbtetci_ptr<MyClass>);
...
std::unique_ptr<MyClass> p = ...
f(p);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM