gsl :: not_null <t *> vs. std :: reference_wrapper <t> vs. T&amp;

时间:2015-10-23 15:48:38

标签: c++ pointers cpp-core-guidelines guideline-support-library

最近提出了{p> C++ Core Guidelines(恭喜!),我担心gsl::not_null类型。如I.12: Declare a pointer that must not be null as not_null中所述:

  

帮助避免解除引用nullptr错误。通过提高性能   避免对nullptr进行冗余检查。

     

...

     

通过陈述意图   源,实现者和工具可以提供更好的诊断,例如   通过静态分析找到一些错误类,并执行   优化,例如删除分支和空测试。

目的很明确。但是,我们已经有了语言功能。不能为null的指针称为引用。虽然引用一旦创建就无法反弹,但std::reference_wrapper解决了这个问题。

gsl::not_nullstd::reference_wrapper之间的主要区别我看到后者只能用于指针,而前者适用于任何nullptr - assignable(引自{{ 3}}):

  

not_null不仅适用于内置指针。它适用于   array_viewstring_viewunique_ptrshared_ptr等   类似指针的类型。

我想象功能比较表如下:

T&

  • 无法存储nullptr? -
  • Rebindable? -
  • 可以用来代替指针以外的东西吗? -

std::reference_wrapper<T>

  • 无法存储nullptr? -
  • Rebindable? -
  • 可以用来代替指针以外的东西吗? -

gsl::not_null<T*>

  • 无法存储nullptr? -
  • Rebindable? -
  • 可以用来代替指针以外的东西吗? -

现在这里是问题,最后:

  1. 我对这些概念之间差异的理解是否正确?
  2. 这是否意味着std::reference_wrapper现在没用了?
  3. PS 我为此创建了代码cpp-core-guidelinesguideline-support-library,我希望这是正确的。

2 个答案:

答案 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