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

时间:2016-11-16 06:23:02

标签: c++ arrays standards smart-pointers implicit-conversion

我正在阅读Effective C ++ 3rd Edition。在第70页,作者说:

  

与几乎所有智能指针类一样,tr1::shared_ptrauto_ptr也会重载指针解引用运算符(operator->operator*),这允许隐式转换为基础原始类指针(...)

然后,他展示了shared_ptr(当时是tr1的一部分)的示例,其中包含基于名为Investment的类的隐式转换:

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

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

好吧,从那时起我就用unique_ptr编写了一些测试用例并且他们坚持了下来。

我还发现了unique_ptr supporting arraysshared_ptr also going to(参见注释)。但是,在我的测试中,隐式转换似乎不适用于数组周围的智能指针。

示例:我希望这个有效...

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

(*test)[0] = 5;

但根据我的编译器(Visual C ++ 2015 Update 3),它不是。

然后,从一个小小的研究中,我发现了一些证据表明隐含的转换根本不受支持......例如:https://herbsutter.com/2012/06/21/reader-qa-why-dont-modern-smart-pointers-implicitly-convert-to

此时我有点怀疑。 支持(标准版),还是不支持?


注意:这本书的主题可能有点过时了,因为作者在第65页也说过,对于dinamically分配的数组,没有像auto_ptrtr1::shared_ptr这样的内容,甚至在TR1& #34;

2 个答案:

答案 0 :(得分:4)

嗯,这就是事情。没有隐式转换到底层指针,你必须调用一个特定的get成员函数(它是标准库中的主题,想想std::string::c_str)。

但那是件好事!隐式转换指针可能会破坏unique_ptr的保证。请考虑以下事项:

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

在上面的代码中,编译器可以尝试将p1的指针传递给p2! (它不会因为这个调用无论如何都会模棱两可,但假设它不是)。他们都会打电话给delete

但我们仍然希望将智能指针用作原始指针。因此,所有运营商都过载了。

现在让我们考虑你的代码:

(*test)[0] = 5;

调用unique_ptr::operator*生成int& 1 。然后尝试在其上使用下标运算符。那是你的错误 如果你有一个std::unique_ptr<int[]>而不仅仅使用句柄提供的operator[]重载:

test[0] = 5;

1 正如 David Scarlett 指出的那样,它甚至不应该编译。数组版本不应该有这个操作符。

答案 1 :(得分:1)

正如StoryTeller所说,隐含的转换会破坏节目,但我想提出另一种思考方式:

unique_ptrshared_ptr这样的智能指针尝试隐藏底层原始指针,因为它们试图在其上维护某种所有权语义。如果你要自由地获取指针并传递它,你很容易违反这些语义。他们仍然提供了一种访问它的方法(get),因为即使他们想要它们也无法完全阻止你(毕竟你可以只关注智能指针并获取指针的地址)。但他们仍然希望设置一个障碍,以确保您不会意外访问它。

虽然一切都没有丢失!通过定义具有非常弱的语义的新类型的智能指针,您仍然可以获得语法上的便利,这样可以安全地从大多数其他智能指针隐式构造它。考虑:

// 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()} {}
};

那么,这个讽刺命名的智能指针是什么意思呢?它只是一种指针,口头上给出了一个合同,用户永远不会保留它或者它的副本超出创建它的表达式,用户也永远不会尝试删除它。由于用户遵循这些约束(没有编译器检查它),隐式转换许多智能指针以及任何原始指针都是完全安全的。

现在你可以实现期望ipiwdostbtetci_ptr的函数(假设它们会尊重语义),并方便地调用它们:

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