重温智能指针没有隐式转换的原因

Revisiting reasons for no implicit conversion on smart pointer

[一个答案,下面是强制在 .release().get() 之间做出选择,遗憾的是错误的一般建议是只使用 .get()]


总结:这道题问的是技术原因,或者行为原因(可能是经验),为什么unique_ptr这样的智能指针被剥离了major指针的特性,i.e. 能够传递到预期指针的位置(C API)。

我研究了这个话题并在下面引用了两个主要的声称原因,但这些似乎都不成立。

但我的狂妄不是无边无际的,我的经验也没有广泛到让我相信我一定是对的。

可能 unique_ptr 没有设计简单的生命周期管理 dumb C API 指针作为主要用途,当然 unique_ptr 的这个建议开发不会 [http://bartoszmilewski.com/2009/05/21/unique_ptr-how-unique-is-it/], however unique_ptr claims to be "what auto_ptr should have been" (but that we couldn't write in C++98)" [http://www.stroustrup.com/C++11FAQ.html#std-unique_ptr] 但也许这也不是 auto_ptr 的主要用途。


我正在使用 unique_ptr 来管理某些 C API 资源,震惊 [是的,震惊 :-)] 发现所谓的智能指针根本不像指针.

API 我使用 expect 指针,我真的 不想到处添加 .get()。这一切都让智能指针 unique_ptr 显得相当愚蠢。

current unique_ptr 在被转换为该指针的类型时没有自动转换为它持有的指针的原因是什么?

void do_something(BLOB* b);
unique_ptr<BLOB> b(new_BLOB(20));

do_something(b); 
// equivalent to do_something(b.get()) because of implicit cast

我读过 http://herbsutter.com/2012/06/21/reader-qa-why-dont-modern-smart-pointers-implicitly-convert-to/ 并且它(考虑到作者)实际上并没有令人信服地回答问题,所以我想知道是否有更多真实的例子或 technical/behavioural 理由来证明这一点。

为了引用文章示例,我没有尝试 do_something(b + 42),并且 + 未在我指向的对象上定义,因此 *b + 42 没有意义.

但如果是这样的话,如果我是认真的,那么我实际上会键入 *b + 42,如果我想将 42 添加到指针,我会键入 b + 42,因为我期待我的智能指针实际上就像一个指针。

使智能指针变笨的原因真的是担心 C++ 编码器不理解如何使用指针,或者会一直忘记遵守它吗?如果我使用智能指针出错,它会静默编译并像使用哑指针一样运行? [肯定争论不休,我可能会忘记->中的>]

对于 post 中的另一点,我写 delete b 的可能性并不比我写 delete b.get() 的可能性大,尽管这似乎是一个普遍提出的理由(可能是因为遗留代码转换),并且讨论 C++ "smart pointer" template that auto-converts to bare pointer but can't be explicitly deleted however Meyers ambiguity of 1996, mentioned in http://www.informit.com/articles/article.aspx?p=31529&seqNum=7 似乎通过为 void*T* 定义一个强制转换来很好地回答这种情况,这样 delete 就可以'不知道要使用哪个转换。

delete 问题似乎 一些合法性,因为在移植一些遗留代码时它可能是一个真正的问题,但它似乎得到了很好的解决甚至在迈耶 (http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/sp_techniques.html#preventing_delete)

之前

是否有更多技术来否定智能指针的这种基本指针行为?还是当时的理由看起来非常有说服力?

以前的讨论包含对坏事的一般警告[ , , C++ "smart pointer" template that auto-converts to bare pointer but can't be explicitly deleted, http://bartoszmilewski.com/2009/05/21/unique_ptr-how-unique-is-it/],但没有具体的警告,或者比使用指向 C API 的非智能 C 指针更糟糕。

编码人员盲目地在任何地方添加 .get() 并得到他们应该受到保护的所有相同危害的隐含风险所带来的不便使得整个限制看起来非常不值得。


在我的例子中,我使用了Meyer的两次转换技巧,并承担了随之而来的隐藏风险,希望读者能帮助我了解它们是什么。

template<typename T, typename D, D F>
class unique_dptr : public std::unique_ptr<T, D> {
    public: unique_dptr(T* t) : std::unique_ptr<T, D>(t, F) { };
    operator T*() { return this->get(); }
    operator void*() { return this->get(); }
};

#define d_type(__f__) decltype(&__f__), __f__

感谢@Yakk 的宏提示 (Clean implementation of function template taking function pointer, )

using RIP_ptr = unique_dptr<RIP, d_type(::RIP_free)>;
RIP_ptr rip1(RIP_new_bitmap("/tmp/test.png"));

不,那是我可以使用的智能指针。 * 我在定义智能指针类型时声明了一次free函数 * 我可以像指针一样传递它 * 当范围结束时它被释放

是的,我可以错误地使用 API 并泄漏拥有的引用,但是 .get() 并不能阻止这种情况,尽管这很不方便。

也许我应该在里面放一些 const 作为对缺乏所有权转让的认可。

unique_ptr::release [http://en.cppreference.com/w/cpp/memory/unique_ptr/release]

的文档中隐含了一个我在搜索中没有找到的答案,这让我很惊讶

release() returns 指针和 unique_ptr 然后引用 nullptr 很明显这可以用于传递 拥有的引用 到不使用智能指针的 API。

据此推断,get()是对应函数传递unowned reference

作为一对,这些函数解释了为什么不允许自动取消引用;编码器被迫使用 替换每个指针 .release().get() 取决于被调用函数将如何处理指针。

因此编码人员被迫采取智能行动并选择一种行为或另一种行为,并将遗留代码升级为更加明确和安全。

.get() 是一个奇怪的名字,但这个解释对我来说很有意义。

遗憾的是,如果编码人员的唯一建议是使用 .get(),则此策略无效;编码器不知道选择,并错过了使他的代码安全和清晰的机会。