为什么 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::optional
、std::variant
、std::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
构造函数。所以它会调用一个匹配的常规构造函数,就像你使用 ()
.
根据[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::optional
、std::variant
、std::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
构造函数。所以它会调用一个匹配的常规构造函数,就像你使用 ()
.