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

[亡魂溺海] 提交于 2019-11-27 19:15:39

References are not pointers that cannot be null. References are semantically very different to pointers.

References have value assignment and comparison semantics; that is, assignment or comparison operations involving references read and write the referenced value. Pointers have (counterintuitively) reference assignment and comparison semantics; that is, assignment or comparison operations involving pointers read and write the reference itself (i.e. the address of the referenced object).

As you noted, references cannot be rebound (due to their value assignment semantics), but the reference_wrapper<T> class template can be rebound, because it has reference assignment semantics. This is because reference_wrapper<T> is designed to be used with STL containers and algorithms, and would not behave correctly if its copy assignment operator didn't do the same thing as its copy constructor. However, reference_wrapper<T> still has value comparison semantics, like a reference, so it behaves very differently to pointers when used with STL containers and algorithms. For example, set<T*> can contain pointers to different objects with the same value, while set<reference_wrapper<T>> can contain a reference to only one object with a given value.

The not_null<T*> class template has reference assignment and comparison semantics, like a pointer; it is a pointer-like type. This means that it behaves like a pointer when used with STL containers and algorithms. It just can't be null.

So, you are right in your assessment, except you forgot about comparison semantics. And no, reference_wrapper<T> will not be made obsolete by any kind of pointer-like type, because it has reference-like value comparison semantics.

I think that there are still use-cases for std::reference_wrapper which are not covered by gsl::not_null. Basically, std::reference_wrapper mirrors a reference and has a operator T& conversion, while not_nullhas a pointer interface with operator->. One use-case that comes to my mind immediatly is when creating a thread:

void funcWithReference(int& x) { x = 42; }
int i=0;
auto t = std::thread( funcWithReference, std::ref(i) );

If I don't have control over funcWithReference, I cannot use not_null.

The same applies to functors for algorithms, and I had to use it for binding boost::signals too.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!