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

3
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)};

不允许使用原始引用数组,所以也许他们将这种语义扩展到了这里? - NathanOliver
我想这个想法是,如果你想要一个引用包装器数组,你应该请求它,而如果你想要一个引用数组,你就不能有一个。 - user253751
1
自从C++20为std::array添加了一个扣除指南并放弃了make_array的想法,也许深入研究这个问题并不值得? - Ted Lyngmo
移除静态断言并通过LSP检查arr的类型,可以发现它构造了一个合法的std::reference_wrapper数组:struct std::array<std::reference_wrapper<int>, 1> - Jacob Faib
1
@NathanOliver 是的,这基本上就是我所做的——移除了 static_assert() ——但是我对移除检查持谨慎态度,因为我看不出它存在的明显原因... - Jacob Faib
显示剩余11条评论
1个回答

2

检查提案N3824确认了我的初步怀疑。

即,static_assert检查被添加以明确禁止从make_array推导出std::array<std::reference_wrapper<T>, N>的类型,因为提案作者认为这种用法容易出错。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&.


2
make_tuple和make_pair对reference_wrapper有特殊处理,因此用户可能希望表达式...也会导致存储T&的类似元组的对象。与“std::make_tuple()”和“std::make_pair()”的关联是缺失的链路。现在明白了作者为什么想要显式选择加入,感谢澄清! - Jacob Faib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接