为什么range :: ostream_iterator默认可构造?

时间:2019-05-10 21:54:47

标签: c++ iterator ostream c++20 range-v3

该问题是在评论here中的讨论之后。

在埃里克·尼布勒(Eric Niebler)的ranges-v3 library(已逐渐成为C ++ 20标准的一部分)中,ranges::ostream_iteratordefault-constructible -没有任何先兆。

为什么?

我认为后来具有有效构造的“虚拟”构造是C ++中的反模式,是我们逐渐摆脱的疣。 std::ostream iterator can only be constructed with a stream(目前-在C ++ 20之前)。而且我们似乎无法使用默认构造的range::ostream_iterator做任何事情……那有什么用呢?

2 个答案:

答案 0 :(得分:6)

这遵循编程元素的设计哲学,即类型应如何表现。如果您听到过短语“像int一样做”,那就是哲学–类型应该是Regular。而EoP definition of Regular是:

  

T的计算基础包括相等性,赋值,析构函数,默认构造函数,复制构造函数,总排序(或默认总排序)和基础类型

转换为真实的C++20 concepts为:

template<class T>
  concept Movable = is_object_v<T> && MoveConstructible<T> && Assignable<T&, T>
    && Swappable<T>;
template<class T>
  concept Copyable = CopyConstructible<T> && Movable<T> && Assignable<T&, const T&>;
template<class T>
  concept Semiregular = Copyable<T> && DefaultConstructible<T>;
template<class T>
  concept Regular = Semiregular<T> && EqualityComparable<T>;

我们已经失去了 total ordering 部分,而只使用EqualityComparable,即使如此,通过Ranges进行的许多库需求实际上只需要Semiregular而不是{ {1}}。但这仍然是这个想法的基础。

请注意,如果类型是可移动的,则默认可构造类型已经很有意义。从概念上移出的状态与默认构造的状态非常相似。从那里不能做很多事情,但这是一种状态。

答案 1 :(得分:3)

在C ++中有很多事情,非默认可构造类型根本无法工作。这是一个非常简单的示例:使用T运算符,从istream提取类型>>,而没有使用默认构造T(否则,给定一个实时T)。您不能,因为接口本身要求存在一个接口。该接口旨在假定您始终可以构造可提取类型的对象。

如果没有给您要使用的对象,则意味着默认构造它。

这似乎是摘自樱桃的例子,但事实并非如此。在通用代码中,您有时只需要创建一个T,以便以后可以填充其中的一部分,这是一种半常见的情况。

然而,我们想说的是,只有当对象对处于这种状态有意义时,它才应该是默认可构造的,这根本不是现实。有时,您只需要立即创建一个对象,以后再用有用的值填充它即可。

因此,Ranges v3库在基本且经常使用的概念SemiRegular中体现了这一要求。这个概念代表了对象操作的一些更基本的方面:我可以做一个,也可以分配它。迭代器必须遵循该概念。


还应注意,在C ++ 20中,ostream_iterator gains a default constructor