【问题标题】:Why ranges::single_view use curly brace initialization for underlying value?为什么 range::single_view 对基础值使用花括号初始化?
【发布时间】:2021-07-29 20:18:30
【问题描述】:

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

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

效果:将value_­ 初始化为value_­{in_­place, std​::​forward&lt;Args&gt;(args)...}­

为什么标准规定使用direct-list-initialization ({}) 来初始化value_?为什么不像std::optionalstd::variantstd::any一样使用direct-initialization (())?

此外,std::constructible_from(std::is_constructible) 指定 T obj(std::declval&lt;Args&gt;()...) 格式正确,而不是 T obj{std::declval&lt;Args&gt;()...}

考虑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&lt;int&gt;{0, 100} 并打印2,而#2 将调用std::vector&lt;int&gt;(0, 100) 并打印100

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

【问题讨论】:

    标签: c++ c++20 range-v3 std-ranges


    【解决方案1】:

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

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


    至于为什么 range 子句普遍使用列表初始化 - 没有充分的理由,它实际上引起了问题。 LWG 批准了一篇论文 (P2367),该论文将消除对具有规范影响的列表初始化的滥用;其余部分由editorial issue 4593 跟踪。

    【讨论】:

    • 错误提交here,感谢您的解释和链接。
    • @TC。那么range-v3也有这个bug吗?还是只是没有相对于标准指定? godbolt.org/z/cGznd3j4T
    【解决方案2】:

    #1 将调用 std::vector{0, 100}

    不,不会的。

    value_ 属于semiregular-box&lt;T&gt; 类型,它是一种仅用于说明的类型,其行为类似于optional&lt;T&gt;,但with some differences。但这些差异不适用于此处,因此您可以将其视为只是 optional&lt;T&gt;

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

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

    【讨论】:

      猜你喜欢
      • 2012-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-14
      • 2020-10-20
      • 2010-12-04
      • 1970-01-01
      • 2022-01-07
      相关资源
      最近更新 更多