Link to home
Start Free TrialLog in
Avatar of BeefJerky8732
BeefJerky8732

asked on

How to determind lvalue vs rvalue

I have a vector class that similar to the STL vector class.
I want to be able to tell the difference between when an item in the vector is being assigned, and when it's being retrieved.

SpecVector<int> myvect(4);
myvect[3] = 99;
V.S.
int foo = myvect[3];

How can I setup my operator[](int) function so that it can determind when it's being called for an lvalue vs rvalue?
Avatar of Mike-
Mike-

I think if you write these:

type &SpecVector::operator[](int i) {
  return m_vector[i];
}

type SpecVector::operator[](int i) const {
  return m_vector[i];
}

(notice that the second one is const and has no reference as return value...)

The first is called for lvalue and the second for rvalue, not sure tho... Can be compiler dependend too.

Why do you need to determine this anyway?
You can always write a getter and a setter with an index value...
Avatar of Axter
You can try the following method:
#include <cstdlib>

template<typename T>
class ElementRef {
public:
      ElementRef(T *i):e(*i){}
      operator T (){printf("lvalue\n");return e;}
      operator=(const T &v){printf("rvalue\n");e = v;}
      T &e;
};

template<typename T>
class Vector  {
public:
      Vector(int Qty, const T &DefaultValue) : m_Size(Qty), m_array(new T[m_Size+1])
      {
            m_array[m_Size] = DefaultValue;
      }
      ~Vector()
      {
            delete [] m_array;
      }
      ElementRef<T> operator[] (int i)
      {
            if (i >= m_Size) i = m_Size;
            return ElementRef<T>(&m_array[i]);
      }
      int m_Size;
      T *m_array;
};


int main(int argc, char* argv[])
{
      Vector<int> v(9, -1);
      int i = 9, j;
      v[4] = 55;
      v[3] = i;
      j = v[4];
      i = v[3];
      printf("Value i = %i, Value j = %i, Value Pass ArrayLen = %i\n", i, j, (int)v[999]);


      system("pause");
      return 0;
}
The best way to do that is to return something which isn't of the type of the element.

template <typename T>
class SpecVector {
private:
   ...blah blah blah...

    T & GetElement(int x);
    const T & GetElement(int x) const;

public:
    class SpecVectorElement;
    friend class SpecVectorElement;

    class SpecVectorElement {
    private:
        int m_x; // index.
        SpecVector & m_vec; // vector.
 
        SpecVectorElement(SpecVector & vec, int x) : m_x(x), m_vec(vec) {}
    public:
        operator T () const { return m_vec.GetElement(m_x); } // used as rvalue

        SpecVectorElement & operator = (const T & val)
         {
            m_vec.GetElement(m_x) = val; // used as lvalue
         }

         friend SpecVector;
     };

    T operator [] (int x) const { return GetElement(x); }
    SpecVectorElement operator [] (int x) { return SpecVectorElement(*this,x); }

   ....other stuff...
};

Now the index operator do not return a T but instead return a SpecVectorElement. This element can be used as r-value in which case it is simply cast to type T using the operator T funcion or it can be used as an l-value using the assignment operator.

This technique can for example also be used so that you can have a bool vector which can take the address of a random element in the bool vector. True, in C you can't take the address of a bit but in C++ you can make a class BitAddress which can hold the address to the byte/int or whatever and the bitnumber in that byte/int to the bit in question.
This class can then be used to hold a bit address and you can overload any pointer operations so that it works properly including assignment so that vec[5] = true; might set bit 5 of a bool vector to true even though the bool vector is implemented by a bit vector and not an array of bool.

Hope this explains.

Alf
Hello,

This is how i do it (its very much in line with mike's)

In short, you use two operators [],
one non const returning a reference to your object (lvalue)
T& operator[] (unsigned p)

and another
const one wich actually copies your object (therefor being const) (rvalue):
T operator[] (unsigned p) const;

Theres however a small trick: the reference operator will always need an object to refer to (ie the 101th element in the array), to sort this out i declare an empty or void (excuse me im not english native) so you can return a reference to this object


template <class T>
class CPrueba {
            T      m_data [100];
            T      m_empty;

      public:

            CPrueba ()  {;}
            virtual ~CPrueba () {;}
            T operator[] (unsigned p) const{
                  return p<100 ? m_data[p] : m_empty;
            }
            T& operator[] (unsigned p){
                  return p<100 ? m_data[p] : m_empty;
            }
};

void main (void){
               CPrueba <int> prueba;

      prueba[0] = 1;
      int valor = prueba[0]; //valor = 1
      prueba[0] = 3;
      valor = prueba[0];//valor = 3
}

hope that helps
Leo
True, returning reference to the element is a simple way to provide l-value. You have no control over the assignment to it though as assignment to it is done by type::operator = ().

If you want to control assignment to the value you need to provide your own type which is used exclusively for this purpose.

klass::my_type klass::operator [] (int k);

Since the object returned is of type klass::my_type the class klass is in control and not whatever type you happen to have as the elements of the container.

Then if you assign to foo[15] = expr the foo[15] will evaluate to an object of type klass::my_type and it is therefore klass::my_type::operator = which is invoked for the assignment of the expr.

As I understood the original question this is what it was asked for.

Alf
Avatar of BeefJerky8732

ASKER

Mike-,
I tried your code, but it didn't work.  At least not on my compiler (VC++ 6.0).

Axter,
So far, your code is the only one that compiles and works for rvalue vs lvalue.
If I don't get a better answer, I'll award you the points.

Salte,
I couldn't get your code to compile.  I got a compile error with the SpecVectorElement & operator= function, which complained about needing a return.
When I tried to add a return, it didn't like that either.
ASKER CERTIFIED SOLUTION
Avatar of Axter
Axter
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>I couldn't get your code to compile.  I got a compile error with the SpecVectorElement & operator= function, which complained about needing a return.
>>When I tried to add a return, it didn't like that either.

What did you use for a return type?  The code is missing "return *this" line.
Here's a version of Salte's code that should compile and give you the same results as the code I posted.

template <typename T>
class Vector_3 {
private:
      T & GetElement(int x){return m_array[x];}
      const T & GetElement(int x) const{return m_array[x];}
      int m_Size;
      T *m_array;
      class SpecVectorElement;
      friend class SpecVectorElement;
      
      class SpecVectorElement {
      private:
            int m_x; // index.
            Vector_3 & m_vec; // vector.
            
        SpecVectorElement(Vector_3 & vec, int x) : m_x(x), m_vec(vec) {}
      public:
            operator T () const { printf("rvalue\n");return m_vec.GetElement(m_x); } // used as rvalue
            
            SpecVectorElement & operator = (const T & val)
        {
                  printf("lvalue\n");
                  m_vec.GetElement(m_x) = val; // used as lvalue
                  return *this;
        }
            
        friend Vector_3;
    };
public:
      Vector_3(int Qty, const T &DefaultValue) : m_Size(Qty), m_array(new T[m_Size+1])
      {
            m_array[m_Size] = DefaultValue;
      }
      ~Vector_3()
      {
            delete [] m_array;
      }
      
      T operator [] (int x) const { return GetElement(x); }
      SpecVectorElement operator [] (int x) { return SpecVectorElement(*this,x); }
};
Thanks Axter

I'm using the class on your second post.  To me it's much cleaner.

By the way, is there any way to get the method that Mike and Guanche posted to work?
When I tried their method, it always runs the same functions.
As all operator = overloads go this one also return a refererence to this. Forgot about that when I wrote the function.

It should be:

SpecVectorElement & operator = (const T & val)
        {
           m_vec.GetElement(m_x) = val; // used as lvalue
           return *this;
        }

That return value should work. Not sure what return value you tried....

Anyway, the fix is only that one line and is kinda classic ;) You will see that "return *this;" is a very common return value from many operators and member functions.

Alf