Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rules for returning fake object reference in C++

I would like to iterate through a pre-allocated float array with a custom container that does not owns the data, but acts on a segment of it. Example, naming the container class 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;

Here's a possible implementation of the 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);
}

Notice that I'm returning a reference from LinhaSobre::operator[] to data that it does not owns. It should not interfere with the data's lifetime (constructors, destructors).

Now I want to expose the stored data by another pattern, std::array<float,4>, and not pure float. Example, naming the new class 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} };

Notice that I treat the items as an array. This would lead to some changes in the container class, my main concern is with the operator[], here's the full class code:

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_;
};

The operator[] returns a reference to a block of memory treated as an std::array<float,4> that never really existed as such, but given the std::array memory layout guaranties, it works. I'm dubious about this, is it OK? (aside from memory alignment, which I'll guarantee). Am I allowed to expose an object like this, semantically? What is the correct term for this? (I've used fake object in the title).

Here's a live demo of the example. Here's another (the other link sometimes fails)

like image 516
Kahler Avatar asked Apr 07 '16 23:04

Kahler


2 Answers

The C++ standard (I'm reading C++11) defines a std::array as follows:

The conditions for an aggregate (8.5.1) shall be met.

You are not guaranteed that a std::array is a POD. The C++ standard guarantees only that it's a class aggregate.

Based on that, I believe that your usage of reinterpret_cast to convert a POD array of floats to a std::array is undefined behavior.

Chances are that it'll work, with your compiler, but you are not guaranteed that this will be portable, or legal.

like image 193
Sam Varshavchik Avatar answered Oct 13 '22 00:10

Sam Varshavchik


You might create a plain old 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;
};
like image 38
bipll Avatar answered Oct 13 '22 01:10

bipll