gsl::not_null
类型。如I.12: Declare a pointer that must not be null as not_null
中所述:
帮助避免解除引用nullptr错误。通过提高性能 避免对nullptr进行冗余检查。
...
通过陈述意图 源,实现者和工具可以提供更好的诊断,例如 通过静态分析找到一些错误类,并执行 优化,例如删除分支和空测试。
目的很明确。但是,我们已经有了语言功能。不能为null的指针称为引用。虽然引用一旦创建就无法反弹,但std::reference_wrapper
解决了这个问题。
gsl::not_null
和std::reference_wrapper
之间的主要区别我看到后者只能用于指针,而前者适用于任何nullptr
- assignable(引自{{ 3}}):
not_null
不仅适用于内置指针。它适用于array_view
,string_view
,unique_ptr
,shared_ptr
等 类似指针的类型。
我想象功能比较表如下:
T&
:
nullptr
? - 是 std::reference_wrapper<T>
:
nullptr
? - 是 gsl::not_null<T*>
:
nullptr
? - 是 现在这里是问题,最后:
std::reference_wrapper
现在没用了? PS 我为此创建了代码cpp-core-guidelines
和guideline-support-library
,我希望这是正确的。
答案 0 :(得分:24)
引用是 not 指针,不能为null。引用在语义上与指针非常不同。
引用具有值赋值和比较语义;也就是说,涉及引用的赋值或比较操作读取和写入引用的值。指针具有(违反直觉)引用赋值和比较语义;也就是说,涉及指针的赋值或比较操作会读取和写入引用本身(即引用对象的地址)。
如你所知,引用不能反弹(由于它们的值赋值语义),但reference_wrapper<T>
类模板可以被反弹,因为它有引用赋值语义。这是因为reference_wrapper<T>
旨在与STL容器和算法一起使用,如果其复制赋值运算符与其复制构造函数的作用不同,则行为不正确。但是,reference_wrapper<T>
仍然具有值比较语义,就像引用一样,因此当与STL容器和算法一起使用时,它的行为与指针的行为非常不同。例如,set<T*>
可以包含指向具有相同值的不同对象的指针,而set<reference_wrapper<T>>
只能包含对具有给定值的一个对象的引用。
not_null<T*>
类模板具有引用赋值和比较语义,如指针;它是一种类似指针的类型。这意味着当与STL容器和算法一起使用时,它的行为类似于指针。它不能为空。
所以,除非你忘了比较语义,否则你的评估是正确的。不,reference_wrapper<T>
不会被任何类似指针的类型淘汰,因为它具有类似引用的值比较语义。
答案 1 :(得分:10)
我认为std::reference_wrapper
仍然存在gsl::not_null
未涵盖的用例。基本上,std::reference_wrapper
镜像引用并进行operator T&
转换,而not_null
具有operator->
指针接口。我想到的一个用例就是在创建一个帖子时:
void funcWithReference(int& x) { x = 42; }
int i=0;
auto t = std::thread( funcWithReference, std::ref(i) );
如果我无法控制funcWithReference
,我就无法使用not_null
。
同样适用于算法的仿函数,我也必须使用它来绑定boost::signals
。