3

According to [range.single.view#3], one of std::ranges::single_view constructors define as:

template<class... Args>
  requires constructible_­from<T, Args...>
constexpr explicit single_view(in_place_t, Args&&... args);

Effects: Initializes value_­ as if by ­value_­{in_­place, std​::​forward<Args>(args)...}­.

Why the standard specifies to use of direct-list-initialization ({}) to initialize value_? Why not use direct-initialization (()) just like std::optional, std::variant, and std::any?

Moreover, std::constructible_from(std::is_constructible) specifies that T obj(std::declval<Args>()...) is well-formed instead of T obj{std::declval<Args>()...}.

Consider the following:

ranges::single_view<std::vector<int>> sv(std::in_place, 100, 0);
std::cout << sv.begin()->size() << "\n";  // #1
std::optional<std::vector<int>>       op(std::in_place, 100, 0);
std::cout << op->size()         << "\n";  // #2

Because different initializations are used, #1 will call the std::vector<int>{0, 100} and print 2, and #2 will call the std::vector<int>(0, 100) and print 100.

Why does the standard specify using curly brace initialization for the underlying value even though it may cause inconsistencies? What is the consideration behind this?

康桓瑋
  • 33,481
  • 5
  • 40
  • 90

2 Answers2

3

value_ is a semiregular-box, so its constructors are well-known and don't include an initializer-list constructor. The inconsistency doesn't arise because in this case braces and parens are equivalent - the underlying type will always be constructed with parens, because that's what the semiregular-box's constructor is specified to do.

The behavior you are observing is a libstdc++ bug.


As to why the ranges clause uses list-initialization pervasively - there's no good reason, and it actually caused issues. LWG has approved a paper (P2367) that will remove misuses of list-initialization with normative impact; the remainder is tracked by editorial issue 4593.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Bug filed [here](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100475), thank you for your explanation and link. – 康桓瑋 May 07 '21 at 15:56
  • @TC. So does range-v3 also have this bug? Or is it just not specified relative to the standard? https://godbolt.org/z/cGznd3j4T – 康桓瑋 May 07 '21 at 16:09
1

#1 will call the std::vector{0, 100}

No, it won't.

value_ is of the type semiregular-box<T>, which is an exposition-only type that acts like optional<T>, but with some differences. But those differences don't apply here, so you can treat it as though it were just optional<T>.

An initializer_list can only be formed from a braced-init-list if all of the types of the expression are the same (or if they're convertible to some type without narrowing). But {in_­place, std​::​forward<Args>(args)...}­ starts with the type std::in_place_t, which is almost certainly not one of the types of Args. So no initializer_list can be formed from it.

And even if a user does provide an in_place_t as Args (perhaps they're doing single_view<optional<T>> or something), it doesn't matter because optional (and therefore semiregular-box) doesn't have an initializer_list constructor. So it would call a matching regular constructor, just like if you'd used ().

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982