c++ 为什么std::optional没有对引用类型进行专门化?

2guxujil  于 2024-01-09  发布在  其他
关注(0)|答案(6)|浏览(230)

为什么std::optionalstd::experimental::optional目前在libc++中)没有对引用类型的专门化(与boost::optional相比)?
我认为这将是非常有用的选择。
在标准库中是否有一些对象 * 引用了
可能
已经存在的对象 * 语义?

1qczuiv0

1qczuiv01#

当n3406(提案的修订版#2)进行了讨论,一些委员会成员对可选引用感到不安。(修订版#3),作者决定将可选引用作为一个辅助建议,以增加可选值获得批准并放入C14的机会。虽然由于各种其他原因,可选值并没有完全进入C14,委员会并不拒绝任择提及,今后如果有人提出,委员会可自由增加任择提及。

ru9i0ody

ru9i0ody2#

std::optional <T&>的主要问题是-optRef = obj在以下情况下应该怎么做:

  1. optional<T&> optRef;
  2. …;
  3. T obj {…};
  4. optRef = obj; // <-- here!

字符串
变体:
1.始终重新绑定-(&optRef)->~optional(); new (&optRef) optional<T&>(obj)
1.通过-*optRef = obj赋值(UB在!optRef之前时)。
1.如果为空则绑定,否则通过-if (optRef) {do1;} else {do2;}赋值。
1.没有赋值运算符-编译时错误“尝试使用已删除的运算符”。
各款产品的优点:

  • 始终重新绑定(由boost::optionaln1878选择):
  • 始终满足!optRefoptRef.has_value()-后置条件**&***optRef == **&**obj时的情况之间的一致性。
  • 与通常的optional<T>在以下方面的一致性:对于通常的optional<T>,如果T::operator=被定义为破坏和构造(一些人认为它 * 必须 * 只不过是破坏和构造的优化),opt = … * 事实上 * 的行为类似于(&opt)->~optional(); new (&opt) optional<T&>(obj)
  • 通过以下方式分配:
  • 与纯T&在以下方面保持一致:对于纯T&ref = …通过赋值(而不是重新绑定ref)。
  • 与通常的optional<T>在以下方面保持一致:对于通常的optional<T>,当opt.has_value()时,需要通过opt = …进行赋值,而不是破坏并构造(参见n3672中的template <class U> optional<T>& optional<T>::operator=(U&& v)on cppreference.com)。
  • 与通常的optional<T>在以下方面保持一致:两者都至少以某种方式定义了operator=
  • 如果为空就绑定,否则赋值--我看不出有什么真实的的好处,恕我直言,只有当#1的支持者与#2的支持者争论时,这个变体才会出现,不管它在形式上与template <class U> optional<T>& optional<T>::operator=(U&& v)的要求更一致(但不符合其精神,恕我直言)。
  • 无赋值运算符(由n3406选择):
  • 与纯T&在以下方面保持一致:纯T&不允许重新绑定自身。
  • 没有模棱两可的行为。

另请参阅:

展开查看全部
8yparm6h

8yparm6h3#

确实有一些东西具有 reference tomaybeexisting object 的语义。它被称为(const)pointer。一个普通的旧的无所有权指针。reference和pointer之间有三个区别:
1.指针可以为空,引用不能。这正是std::optional要避免的区别。
1.指针可以被重定向指向其他东西。让它成为常量,这种差异也就消失了。
1.引用不需要被->*解引用。这是纯粹的语法糖,因为1是可能的。指针语法(解引用并可转换为bool)正是std::optional提供的访问值和测试其存在的语法。

更新:optional是一个值容器。像其他容器(例如vector)一样,它不是 * 设计 * 来包含引用的。如果你想要一个可选的引用,使用指针,或者如果你确实需要一个与std::optional语法相似的接口,为指针创建一个小的(和普通的) Package 器。
**更新2:**至于问题 * 为什么 * 没有这样的专业化:因为委员会只是选择了它。理由 * 可能 * 在文件中找到。这可能是因为他们认为指针就足够了。

gojuced7

gojuced74#

恕我直言,让std::optional<T&>可用是非常好的。然而,关于模板有一个微妙的问题。如果有引用,模板参数可能会变得棘手。
正如我们解决模板参数中引用问题的方法一样,我们可以使用std::reference_wrapper来规避std::optional<T&>的缺失。所以现在它变成了std::optional<std::reference_wrapper<T>>。但是我建议不要使用这种方法,因为1)它太冗长了,既要写签名,(尾随返回类型为我们节省了一点)和它的使用(我们必须调用std::reference_wrapper<T>::get()来获取真实的引用),(二)大多数程序员已经被指针折磨过了,所以当他们收到一个指针时,他们就像本能的React一样,首先测试它是否为空。现在不是什么大问题了

q1qsirdb

q1qsirdb5#

如果让我大胆猜测的话,那是因为std::experimental::optional规范中的这句话。(第5.2节,p1)
如果一个程序需要示例化引用类型的模板optional,或者可能是cv限定的类型in_place_tnullopt_t,那么这个程序就是病态的。

nbnkbykc

nbnkbykc6#

我偶然发现了这个问题好几次,最后我决定实现我的解决方案,不依赖于boost。对于引用类型,它禁用赋值运算符,不允许比较指针或r值。它基于一个类似的work,我以前做过,它使用nullptr而不是nullopt来表示值的缺失。出于这个原因,该类型被称为nullable和编译是禁用指针类型(他们有nullptr反正).请让我知道,如果你发现任何明显或任何不明显的问题与它.

  1. #ifndef COMMON_NULLABLE_H
  2. #define COMMON_NULLABLE_H
  3. #pragma once
  4. #include <cstddef>
  5. #include <stdexcept>
  6. #include <type_traits>
  7. namespace COMMON_NAMESPACE
  8. {
  9. class bad_nullable_access : public std::runtime_error
  10. {
  11. public:
  12. bad_nullable_access()
  13. : std::runtime_error("nullable object doesn't have a value") { }
  14. };
  15. /**
  16. * Alternative to std::optional that supports reference (but not pointer) types
  17. */
  18. template <typename T, typename = std::enable_if_t<!std::is_pointer<T>::value>>
  19. class nullable final
  20. {
  21. public:
  22. nullable()
  23. : m_hasValue(false), m_value{ } { }
  24. nullable(T value)
  25. : m_hasValue(true), m_value(std::move(value)) { }
  26. nullable(std::nullptr_t)
  27. : m_hasValue(false), m_value{ } { }
  28. nullable(const nullable& value) = default;
  29. nullable& operator=(const nullable& value) = default;
  30. nullable& operator=(T value)
  31. {
  32. m_hasValue = true;
  33. m_value = std::move(value);
  34. return *this;
  35. }
  36. nullable& operator=(std::nullptr_t)
  37. {
  38. m_hasValue = false;
  39. m_value = { };
  40. return *this;
  41. }
  42. const T& value() const
  43. {
  44. if (!m_hasValue)
  45. throw bad_nullable_access();
  46. return m_value;
  47. }
  48. T& value()
  49. {
  50. if (!m_hasValue)
  51. throw bad_nullable_access();
  52. return m_value;
  53. }
  54. bool has_value() const { return m_hasValue; }
  55. const T* operator->() const { return &m_value; }
  56. T* operator->() { return &m_value; }
  57. const T& operator*() const { return m_value; }
  58. T& operator*() { return m_value; }
  59. public:
  60. template <typename T2>
  61. friend bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs);
  62. template <typename T2>
  63. friend bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs);
  64. template <typename T2>
  65. friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
  66. template <typename T2>
  67. friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
  68. template <typename T2>
  69. friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
  70. template <typename T2>
  71. friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
  72. template <typename T2>
  73. friend bool operator==(const nullable<T2>& lhs, const T2& rhs);
  74. template <typename T2>
  75. friend bool operator==(const T2& lhs, const nullable<T2>& rhs);
  76. template <typename T2>
  77. friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);
  78. template <typename T2>
  79. friend bool operator!=(const nullable<T2>& lhs, const T2& rhs);
  80. template <typename T2>
  81. friend bool operator!=(const T2& lhs, const nullable<T2>& rhs);
  82. template <typename T2>
  83. friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);
  84. template <typename T2>
  85. friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);
  86. template <typename T2>
  87. friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);
  88. private:
  89. bool m_hasValue;
  90. T m_value;
  91. };
  92. // Template spacialization for references
  93. template <typename T>
  94. class nullable<T&> final
  95. {
  96. public:
  97. nullable()
  98. : m_hasValue(false), m_value{ } { }
  99. nullable(T& value)
  100. : m_hasValue(true), m_value(&value) { }
  101. nullable(std::nullptr_t)
  102. : m_hasValue(false), m_value{ } { }
  103. nullable(const nullable& value) = default;
  104. nullable& operator=(const nullable& value) = default;
  105. const T& value() const
  106. {
  107. if (!m_hasValue)
  108. throw bad_nullable_access();
  109. return *m_value;
  110. }
  111. T& value()
  112. {
  113. if (!m_hasValue)
  114. throw bad_nullable_access();
  115. return *m_value;
  116. }
  117. bool has_value() const { return m_hasValue; }
  118. const T* operator->() const { return m_value; }
  119. T* operator->() { return m_value; }
  120. const T& operator*() const { return *m_value; }
  121. T& operator*() { return *m_value; }
  122. public:
  123. template <typename T2>
  124. friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
  125. template <typename T2>
  126. friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
  127. template <typename T2>
  128. friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
  129. template <typename T2>
  130. friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
  131. template <typename T2>
  132. friend bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
  133. template <typename T2>
  134. friend bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
  135. template <typename T2>
  136. friend bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
  137. template <typename T2>
  138. friend bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
  139. template <typename T2>
  140. friend bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
  141. template <typename T2>
  142. friend bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
  143. template <typename T2>
  144. friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);
  145. template <typename T2>
  146. friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);
  147. template <typename T2>
  148. friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);
  149. template <typename T2>
  150. friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);
  151. private:
  152. bool m_hasValue;
  153. T* m_value;
  154. };
  155. template <typename T>
  156. using nullableref = nullable<T&>;
  157. template <typename T2>
  158. bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs)
  159. {
  160. if (lhs.m_hasValue != rhs.m_hasValue)
  161. return false;
  162. if (lhs.m_hasValue)
  163. return lhs.m_value == rhs.m_value;
  164. else
  165. return true;
  166. }
  167. template <typename T2>
  168. bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs)
  169. {
  170. if (lhs.m_hasValue != rhs.m_hasValue)
  171. return true;
  172. if (lhs.m_hasValue)
  173. return lhs.m_value != rhs.m_value;
  174. else
  175. return false;
  176. }
  177. template <typename T2>
  178. bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
  179. {
  180. if (lhs.m_hasValue != rhs.m_hasValue)
  181. return true;
  182. if (lhs.m_hasValue)
  183. return lhs.m_value != *rhs.m_value;
  184. else
  185. return false;
  186. }
  187. template <typename T2>
  188. bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
  189. {
  190. if (lhs.m_hasValue != rhs.m_hasValue)
  191. return true;
  192. if (lhs.m_hasValue)
  193. return lhs.m_value != *rhs.m_value;
  194. else
  195. return false;
  196. }
  197. template <typename T2>
  198. bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
  199. {
  200. if (lhs.m_hasValue != rhs.m_hasValue)
  201. return false;
  202. if (lhs.m_hasValue)
  203. return *lhs.m_value == rhs.m_value;
  204. else
  205. return true;
  206. }
  207. template <typename T2>
  208. bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
  209. {
  210. if (lhs.m_hasValue != rhs.m_hasValue)
  211. return true;
  212. if (lhs.m_hasValue)
  213. return *lhs.m_value != rhs.m_value;
  214. else
  215. return false;
  216. }
  217. template <typename T2>
  218. bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
  219. {
  220. if (lhs.m_hasValue != rhs.m_hasValue)
  221. return false;
  222. if (lhs.m_hasValue)
  223. return *lhs.m_value == *rhs.m_value;
  224. else
  225. return true;
  226. }
  227. template <typename T2>
  228. bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
  229. {
  230. if (lhs.m_hasValue != rhs.m_hasValue)
  231. return true;
  232. if (lhs.m_hasValue)
  233. return *lhs.m_value != *rhs.m_value;
  234. else
  235. return false;
  236. }
  237. template <typename T2>
  238. bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
  239. {
  240. if (!lhs.m_hasValue)
  241. return false;
  242. return *lhs.m_value == rhs;
  243. }
  244. template <typename T2>
  245. bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
  246. {
  247. if (!lhs.m_hasValue)
  248. return true;
  249. return *lhs.m_value != rhs;
  250. }
  251. template <typename T2>
  252. bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
  253. {
  254. if (!rhs.m_hasValue)
  255. return false;
  256. return lhs == *rhs.m_value;
  257. }
  258. template <typename T2>
  259. bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
  260. {
  261. if (!rhs.m_hasValue)
  262. return true;
  263. return lhs != *rhs.m_value;
  264. }
  265. template <typename T2>
  266. bool operator==(const nullable<T2>& lhs, const T2& rhs)
  267. {
  268. if (!lhs.m_hasValue)
  269. return false;
  270. return lhs.m_value == rhs;
  271. }
  272. template <typename T2>
  273. bool operator!=(const nullable<T2>& lhs, const T2& rhs)
  274. {
  275. if (!lhs.m_hasValue)
  276. return true;
  277. return lhs.m_value != rhs;
  278. }
  279. template <typename T2>
  280. bool operator==(const T2& lhs, const nullable<T2>& rhs)
  281. {
  282. if (!rhs.m_hasValue)
  283. return false;
  284. return lhs == rhs.m_value;
  285. }
  286. template <typename T2>
  287. bool operator!=(const T2& lhs, const nullable<T2>& rhs)
  288. {
  289. if (!rhs.m_hasValue)
  290. return true;
  291. return lhs != rhs.m_value;
  292. }
  293. template <typename T2>
  294. bool operator==(const nullable<T2>& lhs, std::nullptr_t)
  295. {
  296. return !lhs.m_hasValue;
  297. }
  298. template <typename T2>
  299. bool operator!=(const nullable<T2>& lhs, std::nullptr_t)
  300. {
  301. return lhs.m_hasValue;
  302. }
  303. template <typename T2>
  304. bool operator==(std::nullptr_t, const nullable<T2>& rhs)
  305. {
  306. return !rhs.m_hasValue;
  307. }
  308. template <typename T2>
  309. bool operator!=(std::nullptr_t, const nullable<T2>& rhs)
  310. {
  311. return rhs.m_hasValue;
  312. }
  313. }
  314. #endif // COMMON_NULLABLE_H

字符串

展开查看全部

相关问题