【问题标题】:Why does make_optional decay its argument type?为什么 make_optional 会衰减它的参数类型?
【发布时间】:2014-08-24 18:31:48
【问题描述】:

(可能不是 C++14,可能是 Library TS)设施 make_optional 被定义为 (in n3672):

template <class T>
  constexpr optional<typename decay<T>::type> make_optional(T&& v) {
    return optional<typename decay<T>::type>(std::forward<T>(v));
  }

为什么需要转换类型T(即不只是返回optional&lt;T&gt;),是否有哲学(以及实际)理由将decay专门用作转换?

【问题讨论】:

  • 因为 T 可以是左值引用,并且不允许可选引用。
  • @Simple 那么为什么不只是remove_reference
  • 大概是因为你不能同时创建optional&lt;T const&gt;optional&lt;T[N]&gt;,这也会有奇怪的语义。
  • +1 在我在热门问题列表上看到这个问题前二十分钟,我正在编写一个基于通用参考的 make-like 函数。第一次测试时没有decay,我花了十分钟才意识到需要decay 来删除cualifiers 和通用参考崩溃仍然存在...... 为什么不发布你的问题Ecatmur 前一小时??? :P

标签: c++ typetraits optional c++-standard-library c++14


【解决方案1】:

decay 的一般用途是获取一个类型并将其修改为适合存储。

看看这些decay 有效而remove_reference 无效的示例:

auto foo( std::string const& s ) {
  if (global_condition)
    return make_optional( s );
  else
    return {};
}

void function() { std::cout << "hello world!\n"; }
auto bar() { return std::make_optional( function ); }

int buff[15];
auto baz() { return std::make_optional( buff ); }

optional&lt;int[15]&gt; 将是一个非常奇怪的野兽——C 风格的数组在像文字一样处理时表现不佳,这就是 optional 对其参数 T 所做的。

如果您要复制数据,则源的constvolatile 性质无关紧要。而且您只能通过将数组和函数衰减为指针来制作简单的数组和函数副本(而不会退回到std::array 或类似的东西)。 (理论上,可以做一些工作来让optional&lt;int[15]&gt; 工作,但这会带来很多额外的复杂性)

所以std::decay 解决了所有这些问题,并且不会真正引起问题,只要您允许make_optional 推断其参数的类型而不是按字面传递T

如果你想从字面上传递T,毕竟没有理由使用make_optional

【讨论】:

【解决方案2】:

这不是make_optional 的新行为;实用函数 make_pairmake_tuple 的行为方式相同。我可以看到至少有两个理由这样做:

  1. 用未衰减的类型实际实例化模板可能是不可取的或不可能的。

    • 如果T 是一个函数类型,那么你只是不能在一个类中存储一个函数,句号;但是你可以存储一个函数指针(衰减的类型)。

    • 如果T 是数组类型:生成的类将是“有缺陷的”,因为它无法复制,因为数组不可复制分配。对于optionalvalue_or 成员函数根本无法编译,因为它返回T。因此,您根本无法在 optional 中使用数组类型。

  2. 不衰减类型可能会导致意外行为。

    • 如果参数是字符串文字,我个人希望包含的类型是const char*,而不是const char [n]。这种衰减发生在大多数地方,为什么不在这里呢?

    • 如果v 是左值,那么T 将被推断为左值引用类型。例如,我是否真的想要一个包含引用的对,只是因为其中一个参数是左值?肯定不是。

    • pair 或 tuple 或 optional 中的类型或任何不应获得 v 的 cv 限定的类型。也就是说,假设我们将x 声明为const intmake_optional(x) 应该创建一个optional&lt;const int&gt; 吗?不,不应该;它应该创建optional&lt;int&gt;

【讨论】:

    【解决方案3】:

    看看decay做了什么:

    • 删除引用限定符—T&amp;T;没有可选的引用,否则您无法重新安装需要分配的optional

    • 对于数组类型,删除范围—T[42]T*;可选的固定范围数组不是很有用,因为每个数组大小都有不同的类型,并且您不能直接按值传递数组类型,这是valuevalue_or 成员函数工作所必需的。

    • 对于函数类型,添加指针—T(U)T(*)(U);出于类似的原因,没有可选的函数引用。

    • 否则,删除 cv 限定符—const intint;再次没有可选的const 值,否则您无法重新安装optional

    【讨论】:

    • 尝试让optional&lt;int[35]&gt; 工作——这很棘手,因为数组不喜欢在structs 之外复制。
    • 在 #1 上,本规范中确实存在可选引用,本质上是指针:reference implementation。他们可以重新分配;见notes on why it was decided to do so。尽管仅仅因为您 可以 有一个可选的参考并不能使它成为 make_optional 的一个很好的默认值,但根据 @Brian 的回答,为什么它遵循 make_tuplemake_pair 中的约定。
    猜你喜欢
    • 2016-02-17
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 2020-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多