12

是否可以专门std::optional针对用户定义的类型?如果不是,那么提出这个标准是否为时已晚?

我的用例是一个类似整数的类,它表示一个范围内的值。例如,您可以有一个位于 [0, 10] 范围内的整数。我的许多应用程序甚至对单个字节的开销都很敏感,因此std::optional由于额外的bool. 但是,对于std::optional范围小于其基础类型的整数,特化 for 将是微不足道的。我们可以简单地将值存储11在我的示例中。这应该不会为非可选值提供空间或时间开销。

我可以在 中创建这个专业namespace std吗?

4

5 回答 5

11

17.6.4.2.1 [namespace.std]/1 中的一般规则适用:

std只有当声明依赖于用户定义的类型并且特化满足原始模板的标准库要求并且没有明确禁止时,程序才能将任何标准库模板的模板特化添加到命名空间。

所以我会说这是允许的。

NBoptional不会成为 C++14 标准的一部分,它将包含在关于库基础的单独技术规范中,因此如果我的解释有误,有时间更改规则。

于 2013-10-02T10:31:18.913 回答
5

如果您想要一个有效地将值和“无值”标志打包到一个内存位置的库,我建议您查看compact_optional。它正是这样做的。

它不专门化boost::optionalstd::experimental::optional但它可以将它们包装在内部,为您提供统一的界面,在可能的情况下进行优化,并在需要时回退到“经典”可选。

于 2015-10-01T06:37:33.193 回答
2

我问过同样的事情,关于专业化optional<bool>optional<tribool>其他例子,只使用一个字节。虽然没有讨论做这些事情的“合法性”,但我确实认为理论上不应该允许专门化optional<T>与例如:哈希(这是明确允许的)相反的。

我没有日志,但部分原因是接口将对数据的访问视为对指针或引用的访问,这意味着如果您在内部使用不同的数据结构,则访问的一些不变量可能会改变;更不用说提供访问数据的接口可能需要类似reinterpret_cast<(some_reference_type)>. 例如,使用 auint8_t来存储 optional-bool 会对与 的接口optional<bool>不同的接口施加一些额外的要求optional<T>。例如,返回类型应该是什么operator*

基本上,我猜这个想法是为了再次避免整个vector<bool>惨败

在您的示例中,它可能还不错,因为访问类型仍然是your_integer_type&(或指针)。但在这种情况下,简单地设计您的整数类型以允许“僵尸”或“未确定”值,而不是依赖于optional<>为您完成工作,以及额外的开销和要求,可能是最安全的选择。

于 2013-09-29T17:46:00.370 回答
2

轻松选择节省空间

我认为这是一件有用的事情,但是完全专业化的工作比必要的要多一些(例如,变得operator=正确)。

我在 Boost 邮件列表上发布了一种简化专业化任务的方法,尤其是当您只想专业化类模板的某些实例时。

http://boost.2283326.n4.nabble.com/optional-Specializing-optional-to-save-space-td4680362.html

我当前的界面涉及一种特殊的标签类型,用于“解锁”对特定功能的访问。我创造性地命名了这种类型optional_tag。只能optional构造一个optional_tag。对于选择使用节省空间的表示的类型,它需要以下成员函数:

  • T(optional_tag)构造一个未初始化的值
  • initialize(optional_tag, Args && ...)当可能已经存在一个对象时构造一个对象
  • uninitialize(optional_tag)销毁包含的对象
  • is_initialized(optional_tag)检查对象当前是否处于初始化状态

通过始终需要 optional_tag 参数,我们不限制任何函数签名。这就是为什么,例如,我们不能operator bool()用作测试,因为类型可能出于其他原因需要该运算符。

与其他一些可能的实现方法相比,它的一个优点是您可以使其与任何可以自然支持这种状态的类型一起使用。它没有添加任何要求,例如具有移动构造函数。

您可以在以下位置查看该想法的完整代码实现

https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/optional/optional.hpp?at=default&fileviewer=file-view-default

对于使用专业化的类:

https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/class.hpp?at=default&fileviewer=file-view-default

(第 220 至 242 行)

另一种方法

这与我之前的实现形成对比,后者需要用户专门化一个类模板。您可以在此处查看旧版本:

https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/optional.hpp

https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/specialization.hpp

这种方法的问题在于它对用户来说只是更多的工作。用户必须进入一个新的命名空间并专门化一个模板,而不是添加四个成员函数。

在实践中,所有特化都会有一个in_place_t构造函数,将所有参数转发给底层类型。optional_tag另一方面,该方法可以直接使用底层类型的构造函数。

在specializeoptional_storage方法中,用户还有责任为值函数添加适当的引用限定重载。在该optional_tag方法中,我们已经拥有价值,因此我们不必将其拉出。

optional_storage还需要将标准化作为可选的两个辅助类的接口的一部分,用户应该只对其中一个进行专门化(有时将他们的专门化委托给另一个)。

this 和 compact_optional 的区别

compact_optional是一种说法“将这个特殊的哨兵值视为不存在的类型,几乎就像一个 NaN”。它要求用户知道他们正在使用的类型有一些特殊的标记。一种易于optional特化的说法是“我的类型不需要额外的空间来存储不存在的状态,但该状态不是正常值”。它不需要任何人了解优化来利用它;每个使用该类型的人都可以免费获得它。

未来

我的目标是首先将其放入 boost::optional 中,然后是 std::optional 提案的一部分。在此之前,您始终可以使用bounded::optional,尽管它有一些其他(有意的)界面差异。

于 2015-10-03T19:33:44.650 回答
0

我看不出允许或不允许某些特定位模式表示未参与状态如何属于标准涵盖的任何内容。

如果您试图说服库供应商这样做,则需要实施、详尽的测试以表明您没有无意中破坏任何可选(或意外调用的未定义行为)的要求,并进行广泛的基准测试以证明这一点现实世界(而不仅仅是人为的)情况的显着差异。

当然,您可以对自己的代码做任何您想做的事情。

于 2013-10-02T16:47:30.543 回答