为什么 `std::experimental::make_array` 不能使用 `std::reference_wrapper`?

Why can't `std::experimental::make_array` use `std::reference_wrapper`?

template <class D, class...> 
struct return_type_helper 
{ 
  using type = D; 
};

template <class... Types>
struct return_type_helper<void, Types...> : std::common_type<Types...> 
{
  static_assert(
    // why can't I use reference wrappers?
    std::conjunction_v<not_ref_wrapper<Types>...>,
    "Types cannot contain reference_wrappers when D is void"
  );
};

template <class D = void, class... Types>
constexpr std::array<typename return_type_helper<D, Types...>::type, sizeof...(Types)> make_array(Types&&... t) 
{
  return {std::forward<Types>(t)...};
}

void foo()
{
  int x = 7355608;
  auto arr = make_array(std::ref(x)); // does not compile
}

为什么 std::experimental::make_array() 有一个 static_assert() 不允许在自动推导数组类型时使用 std::reference_wrapper?创建引用包装器数组在其他方面是完全合法的,即编译器没有问题

auto arr2 = std::array<decltype(std::ref(x)),1>{std::ref(x)};

检查提案 N3824 证实了我最初的怀疑。

即,添加 static_assert 检查以明确禁止从 make_array 进行类型推导 std::array<std::reference_wrapper<T>, N>,因为提案的作者认为这种用法 error-prone。1

也就是用下面的代码:

auto x = 42;
auto a = make_array(std::ref(x));

用户可以合理地期望 a 的类型为 std::array<int&, 1>,因为 std::ref 在其他上下文中的行为相似。

实际上 std::array<int&, 1> 没有命名有效的类型并且 不能 构造,所以这种混淆的危险是最小的。不过,作者发现这里值得谨慎行事,并使 API 最大限度地防御。

因此,如果用户想要创建一个引用包装数组,他们需要明确请求该类型:

auto a = make_array<std::reference_wrapper<int>>(std::ref(x));

1提案的措辞如下:

Ban reference_wrapper in the make_tuple-like interface. make_tuple and make_pair have special handling of reference_wrapper, then user might expect that the expression

make_array(ref(a), ref(b))

also results in a tuple-like object storing T&.