Home > Software design >  Why can't `std::experimental::make_array` use `std::reference_wrapper`?
Why can't `std::experimental::make_array` use `std::reference_wrapper`?

Time:05-25

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
}

Why does std::experimental::make_array() have a static_assert() that disallows use of std::reference_wrapper when the type of the array is automatically deduced? Creating an array of reference wrappers is otherwise perfectly legal, that is the compiler has no problem with

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

CodePudding user response:

Checking the proposal N3824 confirms my initial suspicion.

Namely, the static_assert check was added to explicitly disallow type deduction of a std::array<std::reference_wrapper<T>, N> from make_array, because the proposal’s author has deemed this usage error-prone.1

That is, with the following code:

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

users could reasonably expect the type of a to be std::array<int&, 1>, because std::ref behaves similarly in other contexts.

In reality std::array<int&, 1> does not name a valid type and cannot be constructed, so the danger of this confusion is minimal. Still, the author found it worth erring on the side of caution here, and making the API maximally defensive.

Consequently, if the user wants to create an array of reference wrappers, they need to explicitly request that type:

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

1 Here’s the proposal’s wording:

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&.

  • Related