区分右值视图和右值容器

时间:2013-07-12 14:16:29

标签: c++ c++11 containers typetraits c++14

我正在编写一些容器或基于迭代的算法。基本上,我对支持for( : )样式迭代的对象进行操作(我很少直接使用for( : ),但请遵循它查找beginend迭代器的方式。

std库中,大多数可迭代对象都是容器。他们都拥有他们的数据,他们让你看看它。

当算法通过右值引用获取容器时,这意味着容器的内容也可以自由获取。例如,如果我写concatinate需要两个vector并返回第三个,如果vector都是move d,我们将要重用第一个vector {1}}然后使用move_iterator s从第二个vector中获取数据,以提高效率。

但是,对于C ++ 1y的string_view和类似概念的类型,我们有可迭代的对象,它们不是容器,而是容器的视图。从语义上讲,我认为视图的行为类似于指针,因此它们的“按值”重复是将视图复制到容器中,而不是它们引用的数据。如果我通过右值引用获取string_view样式视图,这并不意味着它拥有内容:从内容移动是不合理的,只是因为指针本身是右值而移动指针的内容

同时,容器遵循值语义,并且因为它们是rvalue而移动它们的内容是有效的。

对于string_view,这不是问题,但我编写了更多通用的view类,例如contiguous_range_view,可让您对vector的子集进行操作或arrayarr[],好像它是一个不可变大小的缓冲区。

这些视图并不总是将其内容视为const,但他们并不拥有其内容。因此,作为右值的视图并不意味着它们的内容是rvalues!

我的算法如concatinate在这里遇到问题。视图几乎与容器无法区分,并且rvalue容器有效可以移动,而rvalue视图则不能。并且一个按值返回视图的函数是一个很好的模式,因为视图在语义上是指针类型。

我正在寻找一种区分容器和视图的漂亮,干净的方法。是否有计划将C ++ 1y中的string_view区分为我现在可以模拟或挂钩的属性或标记?如果没有,是否有一个好的模式?

如果我设法阻止视图被意外移动,我仍然需要能够有时移动它们,所以我需要一种方法将视图标记为移动候选者而不是被移动右值参考。我怀疑make_move_range函数可能会解决该问题(需要一个可迭代的范围,并将std::make_move_iterator应用于beginend)。

3 个答案:

答案 0 :(得分:2)

选项1:我认为ContainerView概念之间最明显的区别是容器具有inserterase操作,而视图则没有。我试着用这个事实写一个类型特征来检测Container - ness。具体来说,我认为所有标准容器都有void erase(iterator)成员。

选项2:容器管理内存,视图不管理内存。标准容器都有成员typedef allocator_type

值得注意的是,这些观察结果都不适用于std::array或内置数组。

答案 1 :(得分:1)

如果目标是区分std::stringstd::string_view,我会使用以下内容:

template <
class String = std::string, 
class = typename std::enable_if<!std::is_void<decltype(std::declval<String>().substr(0))>::value>::type // Check if the type has the substr member
>
constexpr bool is_view(const String& str = String())
{
    return std::is_nothrow_constructible<String, std::string>::value;
}

作为容器,应该管理内存,它们的构造函数不是没有,而容器视图似乎没有构造函数。

编辑: 感谢Casey的回答,我认为这种检测类型是视图还是容器的元函数(假设视图不是简单的对象而是数组)会起作用。

template <class Type>
constexpr bool is_view()
{
    return (!std::is_trivial<Type>::value) 
        && (!std::is_constructible<Type, 
             std::allocator<typename Type::value_type>>::value);
}

答案 2 :(得分:0)

也许这种一般模式会有所帮助:

struct Container
{
    CopyIterator begin() &;
    CopyIterator end() &;
    MoveIterator begin() &&;
    MoveIterator end() &&;
};

struct View
{
    CopyIterator begin();
    CopyIterator end();
};