为什么make_optional会衰减其参数类型?

时间:2014-07-03 14:52:06

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

(可能不是C ++ 14,可能是库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<T>),并且有一个哲学(以及实际)理由,特别是使用decay转型?

3 个答案:

答案 0 :(得分:16)

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<int[15]>将是一个非常奇怪的野兽 - C样式数组在处理文字时表现不佳,这是optional对其参数T所做的。

如果您要复制数据,则来源的constvolatile性质无关紧要。而且你只能通过将它们衰减为指针来制作数组和函数的简单副本(不会回退到std::array或类似)。 (理论上,可以做的工作使optional<int[15]>起作用,但这会带来许多额外的复杂情况。

所以std::decay解决了所有这些问题,并且不会真正导致问题,只要你允许make_optional推断出它的参数类型而不是字面上传递T

如果您想从字面上传递T,则根本没有理由使用make_optional

答案 1 :(得分:14)

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

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

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

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

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

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

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

    • 对或元组内的类型或可选或任何不应获取v的cv资格的类型。也就是说,我们将x声明为const intmake_optional(x)应该创建optional<const int>吗?不,不应该;它应该创建optional<int>

答案 2 :(得分:6)

看看decay做了什么:

  • 删除参考限定符 - T&T;没有可选引用,否则您无法重置optional,这需要可分配。

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

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

  • 否则,删除cv限定符 - const intint;没有可选的const值,否则您无法重置optional