gsl::not_null<T*> 对比 std::reference_wrapper<T> 对比 T&
gsl::not_null<T*> vs. std::reference_wrapper<T> vs. T&
C++ Core Guidelines has been presented recently (congrats!) and I am concerned about gsl::not_null
type. As stated in I.12: Declare a pointer that must not be null as not_null
:
To help avoid dereferencing nullptr errors. To improve performance by
avoiding redundant checks for nullptr.
...
By stating the intent in
source, implementers and tools can provide better diagnostics, such as
finding some classes of errors through static analysis, and perform
optimizations, such as removing branches and null tests.
意图很明确。但是,我们已经为此提供了语言功能。不能为空的指针称为引用。虽然引用一旦创建就无法重新绑定,但这个问题已由 std::reference_wrapper
解决。
gsl::not_null
和 std::reference_wrapper
之间的主要区别我看到后者只能代替指针使用,而前者适用于任何 nullptr
-可赋值(引自F.17: Use a not_null to indicate that "null" is not a valid value):
not_null
is not just for built-in pointers. It works for
array_view
, string_view
, unique_ptr
, shared_ptr
, and other
pointer-like types.
我想象功能比较 table 如下所示:
T&
:
- 无法存储
nullptr
? - 是
- 可重新绑定? - 否
- 可以用来代替指针以外的东西吗? - 否
std::reference_wrapper<T>
:
- 无法存储
nullptr
? - 是
- 可重新绑定? - 是
- 可以用来代替指针以外的东西吗? - 否
gsl::not_null<T*>
:
- 无法存储
nullptr
? - 是
- 可重新绑定? - 是
- 可以用来代替指针以外的东西吗? - 是
下面是问题,最后:
- 我对这些概念之间差异的理解是否正确?
- 这是否意味着
std::reference_wrapper
现在没用了?
PS 我为此创建了标签 cpp-core-guidelines
和 guideline-support-library
,希望正确。
我认为 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
。
引用是 not 不能为空的指针。引用在语义上与指针非常不同。
引用具有值赋值和比较语义;也就是说,涉及引用的赋值或比较操作读取和写入引用的 value。指针具有(违反直觉的)reference 赋值和比较语义;也就是说,涉及指针的赋值或比较操作读取和写入 reference 本身(即引用对象的地址)。
正如您所指出的,引用不能被反弹(由于它们的赋值语义),但是 reference_wrapper<T>
class 模板 可以 被反弹,因为它具有 reference 赋值语义。这是因为 reference_wrapper<T>
被设计为与 STL 容器和算法一起使用,如果它的复制赋值运算符没有做与其复制构造函数相同的事情,它就不会正确运行。但是,reference_wrapper<T>
仍然具有值 comparison 语义,就像引用一样,因此当与 STL 容器和算法一起使用时,它的行为与指针非常不同。例如,set<T*>
可以包含指向具有相同值的不同对象的指针,而 set<reference_wrapper<T>>
可以包含对具有给定值的一个对象的引用。
not_null<T*>
class模板有引用赋值和比较语义,像一个指针;它是 pointer-like 类型。这意味着当与 STL 容器和算法一起使用时,它的行为类似于指针。它不能为空。
所以,您的评估是正确的,只是您忘记了比较语义。不,reference_wrapper<T>
不会被任何类型的 pointer-like 类型淘汰,因为它具有 reference-like 值比较语义。
C++ Core Guidelines has been presented recently (congrats!) and I am concerned about gsl::not_null
type. As stated in I.12: Declare a pointer that must not be null as not_null
:
To help avoid dereferencing nullptr errors. To improve performance by avoiding redundant checks for nullptr.
...
By stating the intent in source, implementers and tools can provide better diagnostics, such as finding some classes of errors through static analysis, and perform optimizations, such as removing branches and null tests.
意图很明确。但是,我们已经为此提供了语言功能。不能为空的指针称为引用。虽然引用一旦创建就无法重新绑定,但这个问题已由 std::reference_wrapper
解决。
gsl::not_null
和 std::reference_wrapper
之间的主要区别我看到后者只能代替指针使用,而前者适用于任何 nullptr
-可赋值(引自F.17: Use a not_null to indicate that "null" is not a valid value):
not_null
is not just for built-in pointers. It works forarray_view
,string_view
,unique_ptr
,shared_ptr
, and other pointer-like types.
我想象功能比较 table 如下所示:
T&
:
- 无法存储
nullptr
? - 是 - 可重新绑定? - 否
- 可以用来代替指针以外的东西吗? - 否
std::reference_wrapper<T>
:
- 无法存储
nullptr
? - 是 - 可重新绑定? - 是
- 可以用来代替指针以外的东西吗? - 否
gsl::not_null<T*>
:
- 无法存储
nullptr
? - 是 - 可重新绑定? - 是
- 可以用来代替指针以外的东西吗? - 是
下面是问题,最后:
- 我对这些概念之间差异的理解是否正确?
- 这是否意味着
std::reference_wrapper
现在没用了?
PS 我为此创建了标签 cpp-core-guidelines
和 guideline-support-library
,希望正确。
我认为 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
。
引用是 not 不能为空的指针。引用在语义上与指针非常不同。
引用具有值赋值和比较语义;也就是说,涉及引用的赋值或比较操作读取和写入引用的 value。指针具有(违反直觉的)reference 赋值和比较语义;也就是说,涉及指针的赋值或比较操作读取和写入 reference 本身(即引用对象的地址)。
正如您所指出的,引用不能被反弹(由于它们的赋值语义),但是 reference_wrapper<T>
class 模板 可以 被反弹,因为它具有 reference 赋值语义。这是因为 reference_wrapper<T>
被设计为与 STL 容器和算法一起使用,如果它的复制赋值运算符没有做与其复制构造函数相同的事情,它就不会正确运行。但是,reference_wrapper<T>
仍然具有值 comparison 语义,就像引用一样,因此当与 STL 容器和算法一起使用时,它的行为与指针非常不同。例如,set<T*>
可以包含指向具有相同值的不同对象的指针,而 set<reference_wrapper<T>>
可以包含对具有给定值的一个对象的引用。
not_null<T*>
class模板有引用赋值和比较语义,像一个指针;它是 pointer-like 类型。这意味着当与 STL 容器和算法一起使用时,它的行为类似于指针。它不能为空。
所以,您的评估是正确的,只是您忘记了比较语义。不,reference_wrapper<T>
不会被任何类型的 pointer-like 类型淘汰,因为它具有 reference-like 值比较语义。