为什么 ranges::single_view 对基础值使用大括号初始化?

Why ranges::single_view use curly brace initialization for underlying value?

根据[range.single.view#3], one of std::ranges::single_view构造函数定义为:

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)...}­.

为什么标准规定使用direct-list-initialization ({}) to initialize value_? Why not use direct-initialization (()) just like std::optionalstd::variantstd::any

此外,std::constructible_from(std::is_constructible) 指定 T obj(std::declval<Args>()...) 是合式的,而不是 T obj{std::declval<Args>()...}

考虑 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

因为使用了不同的初始化,#1会调用std::vector<int>{0, 100}并打印2,而#2会调用std::vector<int>(0, 100)并打印100.

为什么标准指定对基础值使用花括号初始化,即使它可能会导致不一致?这背后的考虑是什么?

value_ 是一个 semiregular-box,因此它的构造函数是众所周知的并且不包含初始化列表构造函数。不会出现不一致,因为在这种情况下大括号和括号是等价的——底层类型将始终用括号构造,因为这就是指定 semiregular-box 的构造函数要做。

您观察到的行为是一个 libstdc++ 错误。


至于为什么 ranges 子句普遍使用列表初始化 - 没有充分的理由,它实际上导致了问题。 LWG 已批准一份文件 (P2367),该文件将消除具有规范影响的列表初始化的滥用;其余部分由 editorial issue 4593.

跟踪

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

不,不会。

value_ 属于 semiregular-box<T> 类型,它是一种仅限说明的类型,其行为类似于 optional<T>,但 with some differences。但这些差异在这里不适用,因此您可以将其视为只是 optional<T>.

如果表达式的所有类型都相同(或者如果它们可以转换为某种类型而不缩小),则只能从花括号初始化列表形成 initializer_list。但是 {in_­place, std​::​forward<Args>(args)...}­ 以类型 std::in_place_t 开头,这几乎可以肯定不是 Args 的类型之一。所以没有initializer_list可以从它形成。

即使用户确实提供 in_place_t 作为 Args(也许他们正在做 single_view<optional<T>> 或其他事情),也没关系,因为 optional (因此 semiregular-box)没有 initializer_list 构造函数。所以它会调用一个匹配的常规构造函数,就像你使用 ().