-2

I'm trying to create a custom vector class for use in doing linear algebra calculations in my programs. The class name for now is Rvec. Here's what I'm attempting:

I want to be able to reference elements in my vector by index BUT, also want to be able to access the first three elements (x,y,z) by typing .x after an object name (like if I had and Rvec named V, I could access element 0 by typing V.x and similar for y and z) because that makes it much more easy to follow in some of my programs. I've been thinking about this for a while and also couldn't find any definitive answers online but though of a few ideas that all ended in failure.

I also overloaded the [] operator so that you can access elements directly (V[0] instead of V.values[0])

class Rvec {
public:
    std::vector<float> values;
    float operator[] (int index) { return values[index]; }

    //option 1
    //float x, y, z = values[0], values[1], values[2];

    /*  option 2
    float* x = &values[0];
    float* y = &values[1];
    float* z = &values[2];

        // *Rvec.x
    */

    /*  option 3
    #define x values[0]
    #define y values[1]
    #define z values[2]

        // Rvec.x
    */

    //  option 4

 /*   std::map<char, float> m;
    Rvec() {
        m.insert(pair<char, float>('x', 0));
        m.insert(pair<char, float>('y', 0));
        m.insert(pair<char, float>('z', 0));
    }
    float operator[](int index) {return m.at(index);}
    float operator[](char key) { return m[key]; }*/
};

The first option would just to be initialize x,y,z as floats, but that clearly wont work because they won't update their values when the actual elements do.

The second option would be to define x,y,z as float pointers but then I would need to access them by typing *V.x and so on and that isn't the thing I'm going for.

The third option would be to #define x,y,z as macros. But, that comes with its own issues that I don't want to deal with.

The final option might be to use a map and initialize the keys to the first three elements as x,y,z. But then to reference them after overloading it would be V['x'] which is even worse than pointers.

Is there any way to be able to reference elements like this just typing V.x and the value always being the same as the element it corresponds to?

  • Take a look at [What are the basic rules and idioms for operator overloading?](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) to see why `float operator[] (int index)` is probably not what you want. – Drew Dormann Oct 24 '22 at 22:50
  • @DrewDormann I realise i may not be using any idoms for coding (i don't normally use them because i havn't work on projects with others yet. sorry if that is an inconvenience. besides, i've overloaded operators in that exact way before and it works exactly as i intended. the overloading of operators is also not what my question was about so please read it. – WizardMorpheus Oct 24 '22 at 22:59
  • Make x/y/z proxy classes that has a conversion operator to float. Or you can have them be normal floats, then use the vector for every element after the three first. Compensate by making your `operator[]` "do the right thing". – super Oct 24 '22 at 23:02
  • This operator doesn't work as expected, `Rvec myVec; myVec.values.push_back(1); myVec[0] = 2;` will not update the value in vector. That aside, it sounds like you want references, but having those as members comes with drawback (e.g. no more move assignement for you, you will have to write one yourself). – Yksisarvinen Oct 24 '22 at 23:05
  • @Yksisarvinen Additional issue with references is that you can only safely reference elements in the vector if you know it won't reallocate. – super Oct 24 '22 at 23:06
  • @super Oh, right, I forgot about that. Yeah, that kinda invalidates my idea. – Yksisarvinen Oct 24 '22 at 23:07
  • @super thank you. that is quite a smart solution – WizardMorpheus Oct 24 '22 at 23:09
  • Note: There is often a huge gulf between "can do" and "should do". C++ gives you a lot of rope should you want to hang yourself. – user4581301 Oct 24 '22 at 23:09

2 Answers2

1

I recommend using some getters and setters:

class Rvec
{
  public:
    // getter
    float x() const
        { return values[0];}

    // setter
    void x(float new_value)
    {  values[0] = new_value;}

    // If you trust the callers you return a reference:
    float& y()
    {  return values[1]; }

};

This would appear in code as:

Rvec r;
//...
std::cout << r.x() << "\n";

float f = 0.0;
std::cout << "Enter x value: ";
std::cin >> f;
r.x(f);

std::cout << "Enter y value: ";
std::cin  >> r.y();

The syntax may not be as elegant as other languages that support properties, but it should resolve your issue.

Note: if the last pair of statements doesn't work try this:

std::cout << "Enter y value: ";
std::cin >> f;
r.y() = f;

The last statement is valid since the r.y() returns a reference to a vector slot.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • sorry if i didn't make it clear. i know you can do this but, i was wondering if there was any way to only ty .x or .y not .x(). thanks though – WizardMorpheus Oct 24 '22 at 23:08
0

A good solution that was given by @super in the comments:

create x,y,z as regular float, use the values vector for all elements past the first three, then make the [] operator fetch the correct variable depending on index.

class Rvec {
public:
    float x, y, z;
    std::vector<float> values;
    float operator[] (int index) { 
        switch (index) {
        case 0:
            return x;
        case 1:
            return y;
        case 2:
            return z;
        default:
            return values[index - 3];
        }
    }
};