缺失 shared_ref

5
在使用std::shared_ptr时,我有点想念一个shared_ref实现。这是shared_ptr的一个特化版本,它保证永远不会包装一个nullptr(当然,前提是正确使用)。
我好奇为什么它没有出现在C++11标准中。是否有任何重大问题需要解决以实现它?在我脑海中,我想不出任何问题。
编辑:
我希望有一个类似于以下接口:
template <typename T>
class shared_ref {
public:
  shared_ref( T&& ref );
  T& get();
  T* operator&() const;

  template< class Y > 
  void reset( Y&& obj );

  long use_count() const;
  bool unique() const;

  void swap( shared_ref& r );
};

3
当使用默认构造函数时,shared_ptr 将会是 nullptr - abergmeier
3
重点不是这个,问题在于如果一个函数被传递了一个 shared_ptr,并不能保证它不是空指针。如果你真的想写出正确的代码,在使用指针之前必须检查是否为 nullptr。但鉴于 95% 的程序员并不关心这一点。 - abergmeier
1
@LCIDFire 你不能从错误的代码中得到正确的代码。 - R. Martinho Fernandes
5
@R.MartinhoFernandes,问题是如何最好地执行合同,该合同是指针不为空。您越早捕捉到这样的错误越好。黄金标准是严格的类型检查,在编译时捕获错误。 - Mark Ransom
5
这正是我想要使用 shared_ref 的原因! - abergmeier
显示剩余5条评论
2个回答

3

实施它时会遇到什么主要问题吗?

这是其中一个:你无法拥有引用。智能指针的全部意义在于声明对指针本身的所有权。由于你无法控制引用的生命周期,因此 shared_ref 是行不通的。

而且,这也行不通:

shared_ref( T&& ref ) : p(&ref) {}

用户可能已经给你一个堆栈变量,这意味着你现在拥有了这个对象和堆栈变量之间的“共享”所有权。而且堆栈变量无法共享所有权。
你只能控制指针的生存周期。指针可以是NULL。因此,你唯一能做的就是在运行时检查指针是否为NULL。
你能做的最好的方法就是创建一个与shared_ptr等效的接口,除了它没有默认构造函数并在收到NULL时抛出异常。难道值得为此创建一个全新的指针类型吗?
C++核心准则支持库有not_null模板,可应用于大多数类似指针的类型。因此,当你想要验证指针不是NULL时,可以使用not_null<shared_ptr>,但仅在首次使用时检查一次。创建指针后,无需再次检查。
尽管你不能强制别人使用它们,但始终使用该类型将解决问题。

1
你说得对。我可能会通过要求 T 具有移动构造函数来解决这个问题。然后,您可以在内部构建一个指针并确实拥有所有权。这种冗长的要求可能是为什么它不在标准库中的原因。 - abergmeier
2
也许名称不正确 - 或许shared_obj是更正确的名称!? - abergmeier
仍然有价值支持非空指针的语言支持(从技术上讲,所有指针都可以为null,但在这种元类型上进行编译时检查仍然是可能的,就像Swift所做的那样)。需要执行的唯一两个检查是:1.确保没有直接的nullptr赋值,2.确保在尝试将可选指针分配给非可选指针之前对其进行解包。 (所有这些都可以在编译时验证)。 PS:你仍然可以搞乱内存,但这取决于你。 - diegoreymendez
请查看此答案,了解“解包”是什么 - 在C++中也完全可行:https://dev59.com/g2Ag5IYBdhLWcg3wDHQ3 - diegoreymendez
1
关于“绝对最佳”段落,如果显式调用reset(nullptr),它也必须具有不同的行为。我并不完全同意前面的段落:shared_ptr管理对象的生命周期,而不是指针。它可能使用指针作为其内部表示的一部分,但这并不重要。从概念上讲,容器可以管理共享对象的生命周期,而不允许没有当前正在管理的对象;但那只是一个不同于shared_ptr的类。 - M.M
显示剩余7条评论

0

shared_ptr 只有两种情况会为空 - 要么它是默认构造的,要么在某个时刻被赋予了空值。既然您已经同意默认构造您的假想 shared_ref 类没有意义,那么只剩下第二种情况。

如果您尝试将 nullptr 分配给您的 shared_ref 对象,您会期望发生什么?它应该抛出一个错误吗?使用一个简单的模板函数,使用常规的 shared_ptr 可以轻松完成相同的操作:

template<typename T>
T* notnull(T* ptr)
{
    if (ptr == std::nullptr)
        throw std::invalid_argument(std::string("nullptr"));
    return ptr;
}

std::shared_ptr<int> pint = notnull(GetIntPtr());

通常情况下,除非存在迫切需要且没有简单的解决方法,否则不会将事物添加到标准中。


1
请看我的编辑。我可能也会允许指针的重载,但当发现指针为空时,它们必须抛出异常。 - abergmeier
@LCIDFire,我再问一遍:当您尝试将nullptr分配给此对象时,应该发生什么?即T* p = nullptr; my_ref.reset(*p); - Mark Ransom
唯一真正的问题是获取引用“更容易”有点棘手。调用get()确实有点冗长:( - abergmeier
也就是说,像标准库中的任何地方一样,这是未定义行为。 顺便提一下,这是调用代码中的错误。 - abergmeier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接