在C++中返回伪对象引用的规则



我想用一个自定义容器遍历一个预先分配的浮点数组,该容器不拥有数据,但作用于其中的一段。例如,将容器类命名为LinhaSobre:

std::unique_ptr<float[]> data(new float[720]);
...
//creates container to iterate 26 floats starting from from data[12]
    LinhaSobre cont(data.get()+12, 26); 
//sets those elements to 1.5
    for(size_t i = 0; i < cont.size(); i++)
        cont[i] = 1.5f;

以下是operator[]:的可能实现

//...
//LinhaSobre has a member mem0 which is initialized
//as a pointer to where the interval starts
float & LinhaSobre::operator[] (size_t i)
{
    return *(mem0+i);
}

请注意,我将从LinhaSobre::operator[]返回一个对它不拥有的数据的引用。它不应该干扰数据的生存期(构造函数、析构函数)。

现在,我想通过另一种模式std::array<float,4>公开存储的data,而不是纯的float。示例,命名新类LinhaSobre4f:

std::unique_ptr<float[]> data(new float[720]);
...
//creates container to iterate 4 array<float, 4> starting from from data[12]
    LinhaSobre4f l(data.get()+(3*4), 4);
//sets those elements to {1.5f, 2.5f, 3.5f, 4.5f};
    for(size_t i = 0; i < l.size(); i++)
        l[i] = { {1.5f, 2.5f, 3.5f, 4.5f} };

请注意,我将这些项视为一个数组。这将导致容器类的一些更改,我主要关心的是operator[],下面是完整的类代码:

struct LinhaSobre4f
{
    LinhaSobre4f(float * pos_begin, size_t size_):
        pos0(pos_begin),
        size_(size_){}
    std::array<float, 4> & operator[](size_t i)const
    {
        std::array<float,4> * r = 
            reinterpret_cast<std::array<float,4>*> (pos0+(4*i));
        return *r;
    }
    size_t size()const
    {
        return size_;
    }
private:
    float * pos0;
    size_t size_;
};

operator[]返回对被视为std::array<float,4>的内存块的引用,该内存块从未真正存在过,但在std::array内存布局保证的情况下,它可以工作。我对此持怀疑态度,可以吗?(除了内存对齐,我保证)。我可以从语义上公开这样的对象吗?正确的术语是什么?(我在标题中使用了假对象)。

下面是该示例的现场演示。这是另一个(另一个链接有时会失败)

C++标准(我正在阅读C++11)定义std::array如下:

应满足骨料的条件(8.5.1)。

不能保证std::array是POD。C++标准只保证它是一个类聚合。

基于此,我认为您使用reinterpret_castfloats的POD数组转换为std::array是未定义的行为。

它很可能与编译器一起工作,但不能保证它是可移植的或合法的。

您可以创建一个普通的旧reference_type:

struct LinhaSobre4f {
    struct Ref {
        Ref(float *m): m(m){};
        Ref &operator=(std::initializer_list<float> const &l) {
            std::copy(l.begin(), l.end(), m);
            return *this;
        }
    private:
        float *m;
    };
    Ref operator[](size_t i) { return m + 4 * i; }
private:
    float *m;
};

再加上Sam Varshavchik的回答,您可能会对span类型(以前称为array_view)感兴趣。

span类型是一种抽象,它提供了对连续对象序列的视图,对象序列的存储由其他对象所有(更多详细信息,请参阅P0122R1,CppCoreGuidelines和Guidelines Support Library Review:span<T>)。

从概念上讲,span只是指向某个存储的指针和可通过该指针访问的元素的计数。它太小了,可以通过值传递。

开放源代码(仅限标头)的引用实现可在https://github.com/Microsoft/GSL(该实现通常假设一个实现C++14支持的平台。有特定的解决方案来支持MSVC 2013和2015)。

相关内容

最新更新