首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >一种用于C++的切片类型

一种用于C++的切片类型
EN

Code Review用户
提问于 2015-12-29 11:06:34
回答 2查看 2.3K关注 0票数 3

Go编程语言有这个切片类型,它是一个指向实际数组的指针。这允许将子数组作为实际数组来处理,几乎没有性能开销。这个代码片段是关于为C++数组和向量实现片类型的。

片.h

代码语言:javascript
复制
#ifndef SLICE_H
#define SLICE_H

#include <sstream>
#include <stdexcept>
#include <string>

template<class T>
class slice {
    private:
        T*     m_array;
        size_t m_length;

    public:
        slice(T* array, size_t length) {
            this->m_array  = array;
            this->m_length = length;
        }

        slice(T* array, size_t length, size_t skip) {
            this->m_array = array + skip;
            this->m_length = length;
        }

        T& operator[](size_t index) { 
            if (index >= m_length) {
                std::ostringstream os;
                os << "Bad index: " << index << ", slice size: " << m_length;
                throw std::runtime_error(os.str());
            }

            return m_array[index];
        }

        size_t size() {
            return m_length;
        }

        T* begin() {
            return m_array;
        }

        T* end() {
            return m_array + m_length;
        }

        const T* begin() const {
            return m_array;
        }

        const T* end() const {
            return m_array + m_length;
        }
};

#endif  /* SLICE_H */

main.cpp

代码语言:javascript
复制
#include <algorithm>
#include <iostream>
#include <vector>

#include "slice.h"

using std::cout;
using std::endl;
using std::vector;

int main(int argc, char** argv) {
    int int_array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    //// Should be 4, 5, 6, 7, 8.
    slice<int> pizza_slice = slice<int>(int_array, 5, 3);

    cout << "Arrays slice size: " << pizza_slice.size() << endl;

    for (int i : pizza_slice)
    {
        cout << i << endl;
    }

    vector<int> int_vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

    //// Should be 2, 3, 4
    pizza_slice = slice<int>(&int_vector[0], 3, 1);

    cout << "Vector slice size: " << pizza_slice.size() << endl;

    for (int i : pizza_slice) 
    {
        cout << i << endl;
    }

    return 0;
}

任何批评都是非常感谢的。

EN

回答 2

Code Review用户

回答已采纳

发布于 2015-12-29 15:00:45

这个看起来不错。

使用mem-初始化程序列表

当你写

代码语言:javascript
复制
slice(T* array, size_t length) {
    this->m_array  = array;
    this->m_length = length;
}

默认情况下,构建m_arraym_length,然后分配它们。对于指针和积分类型,这并不是非常昂贵的,但这是一种浪费,最好不要养成这种习惯。更喜欢直接构造:

代码语言:javascript
复制
slice(T* array, size_t length)
: m_array(array)
, m_length(length)
{ }

另一方面,您的另一个构造函数可以委托给这个构造函数:

代码语言:javascript
复制
slice(T* array, size_t length, size_t skip)
: slice(array + skip, length)
{ }

但另一方面,无论如何也不需要存在。让用户进入他想要开始的地方。我觉得这只是有点臃肿。

const函数

size()应该是const,而你错过了T const& operator[](size_t) const

您还可以提供一个T* data()T const* data() const,在本例中只是别名begin()

另外,考虑提供一个不同的构造函数,以便我可以从一个slice<const T>构造一个slice<T>。这是有意义的。

投掷

如果您是因为超出范围的索引而抛出的,则应该抛出std::out_of_range。但是通常,避免operator[]抛出。这是你最常使用的东西,所以最好尽量简单。如果您想提供抛出替代方案,标准库所遵循的典型模式是提供一个at()函数,该函数执行边界检查,然后转发到operator[]

这里的一切都可以是constexpr。你就过火吧。

票数 5
EN

Code Review用户

发布于 2015-12-29 21:12:35

这是一个好的开端,但你做得还不够:

您的类型基本上是一个范围,但仅限于由指针表示的连续类型。

虽然我想知道为什么您认为一个通用的构建块有任何业务生产任何输出没有明确要求?

而且,如果你使用的是制造功能的话,它会更加舒适。

如果您泛化了它,那么使用mem初始化器来初始化您的成员,而不是在ctor-body中分配它们,可能会产生不同的效果。

而且,您的许多成员并不是noexcept,即使他们从不抛出,即使他们很可能是constexprsize const也不是,即使它没有变异。

如前所述,抱怨超限指数的正确方法是抛出std::out_of_range

不过只有在那些情况下你才应该检查它们。

考虑使用assert进行额外的检查,这些检查只应在调试-构建中完成。

您还缺少了成员函数cbegin()cend()和用于反向迭代器的函数,这些函数是空范围的默认构造函数、成员类型value_typesize_typedifference_typereferenceconst_referencepointerconst_pointerreverse_iteratorconst_reverse_iterator

关于你的测试套件:

  • 除非您可能需要显式手动刷新,否则不要使用std::endl。如果你不需要它,那就不必要地扼杀了你的表现。
  • return 0;隐含在main中。
  • 我不知道你为什么在你的测试套件中使用std::vector,但你可能有一个很好的理由.
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/115319

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档