overloaded assignment operator and different data types

Pages: 12
I have another question about assignment operator overloading.

Let's say I have a class...

1
2
3
4
5
6
7
8
class S
{
   int a, b, c;
public:
   <other stuff>
}

S s;


...and I'd like to be able to overload the assignment operator so that a statement like:

s = 0;

So that s.a, s.b and s.c are all assigned 0.

Is this possible?
Yes.
Write a S& S::operator = (int i);
Thanks, hamsterman. That solves the problem of an individual assignment. Now, it would be useful (but not essential) if this supported chaining.

The trick in chaining this operator is that the assignment operator for the class works a little strange. If:

1
2
3
4
5
6
7
8
9
10
11
12
class S
{
   int a, b, c;
public:
   <other stuff>
}

S s, t;
.
.
.
t = s;


then the assignment operator will do this:

t.c = s.a;

There may be no way around this one, which is OK; I'm just pushing the envelope (for me, at least).

Thanks again for the help.
Assignment operator is almost a normal function. Whatever you are able to do in any other function you can do in assignment operator.
closed account (zwA4jE8b)
1
2
3
4
S& S::operator = (int i)
{
    Do your stuff here.. like assign i to all three variables.
}


thanks again hamster for your help with my other question!
Last edited on
The problem I run into with chaining is that I want the two assignment operators behave differently.

The assignment operator with a class argument only does the t.c = s.a; operation.

The assignment operator with an int argument applies the int to *all fields* of the class.

So, if I have a line of code:

s = t = 0;

All fields in t will be zeroed, but only s.a will be zeroed.

As I said, this is a very unusual problem; I'm OK with using a function call to assign int values to these classes. Or, I guess I can just have a rule not to chain such assignments.

Still, if anyone has any magic ideas, I'm all ears.
It sounds like you are abusing the assignment operator.

If behavior is not intuitive, then you shouldn't be doing it. Furthermore, behavior should be transitive.

Example:

1
2
3
4
5
6
7
8
9
10
11
S a, b;

// this code:
b = 0;
a = b;

// and this code:
b = 0;
a = 0;

// should both be 100% functionally equivilent 


If the above are not equivilent, then your assignment operators are poorly defined.



The whole point of assignment operators is to make code more intuitive. They're not there to be "cool". Don't use them just because you can -- only use them when it makes sense to.

Here, it sounds like it doesn't make sense to overload them in this way. So don't.
closed account (zwA4jE8b)
You can do either or.

1
2
3
4
5
6
S& S::operator = (const S& otherobj)
{
   a = otherobj.a;
   b = otherobj.b;
   c = otherobj.c;
}
Last edited on
I suppose you could have your operator = (int) return an int.
Though listen to Disch, this is abuse.
Thanks for the input, guys. I guess I was trying to perform an unnatural act here. I'll just make some conventional member functions for setting the values within the object.
I'd like to ask another, hopefully simpler question. Suppose I have a class S, as defined above, and I'd like to be able to do assignment overloads to and from integer values, for example:
1
2
3
4
5
6
7
8
S reg;
int i;

i = reg; // i gets assigned the value reg.b
.
and/or
.
reg = i; // reg.b gets assigned the value of i 


Is this a reasonable thing to do?

EDIT: I guess if I can't do this via assignment overloads, the fallback plan is to use functions like getvalue () and setvalue(int x). All suggestions are welcome.
Last edited on
I think it's a terrible idea. Why would you want to do that?
If you wonder why this is a terrible idea, imagine assigning a circle to an integer to get the radius. It's random, confusing and doesn't make any sense to the user.
OK...maybe I should explain what I'm trying to do here.

The class I'm trying to create represents a register. Its value is state-dependent, and as such, I need to keep three variables within the object, check some external state variables, and return the correct value accordingly. But, to the outside world, I want this to appear as much like a simple integer as possible.

I thought a int=object assignment would appear simpler to the user than a function call, but...I guess people here disagree.
You can't overload int::operator =, but you can write reg::operator int()
Last edited on
I suppose your plan to overload the = and (int) casting operators does make a bit more of sense in this context.. except for the problem of it not being transitive.

Although I'm trying to comprehend how a register can have multiple states at one time... can you explain this further? If not that's fine -- it's just to satisfy my curiosity.
Hi, Disch -

The rationale behind this implementation is somewhat arcane. I'm writing a simulator for an ASIC. One of the issues we face is that in an ASIC, every circuit processes simultaneously. So, to simulate this, we need to keep track of multiple states (current, next, previous), and process accordingly whether the clock is high or low.

I realize that this is by no means the most efficient or clearest way to code this, but the goal of the program is to very faithfully simulate the actual operation of the ASIC circuitry. So, if I appear sometimes to be trying to perform unnatural acts, it may be because of this. (Then again, it may be because I'm still a C++ newbie.)

If anyone has a reference to more information about what hamsterman suggested above, I'd love to see it.

Thanks.
In order to make the object as amorphous to int as possible, from the user perspective, you need to provide the following definitions:
1
2
3
4
5
struct S {
  S(int) { /*...*/ }
  int operator= (int right) { /*...*/ return (int)(*this); } //optional, for improving performance
  operator int() { return 0/*substitute...*/; }
};

I made wanted to make the assignment return reference, because:
Standard wrote:
The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue.
And I think that they mean assignable lvalue. But it didn't work. See the edits.

On a side note, I think you should use properties. They are usually handled in C++ with getters and setters, and this is not the most graceful solution, but it is the best available. Your register is not an int, and making it an int, so that it is convenient in specific context is not elegant. Next, you'll be making it bool for convenience in some other context, and your object will become the chimera of software design. Sometimes it is useful, but it is apparently cheating.

There is alternative way to implement properties. You could also use sub-object for the property that keeps pointer to the host object ("parent pointer") and in this way establishes two-way communication. This allows the host object to update its state when the sub-object is updated. The sub-object can be amorphous to int and it can provide the user with alternative personality for your main object. But this parent pointer business is redundant, and with proper support for properties in C++, it could have been avoided entirely.

Regards

EDIT: Messed up the assignment. Had to return the left operand, not the right one.
EDIT: The assignment operator is tough in this case. If you return the right side as lvalue, you will not follow the required protocol, and the operand on the right has to be lvalue. If you return the left side, it is not int. So finally, I decided to return the left side converted to int, and not as reference. This is slight deviation from the conventional case.
Last edited on
Thanks for the detailed reply, Simeonz. So, distilling down what you've said, it's probably best to let the users access it through gets and sets?

Easy for me!
Technicalities aside (which I myself didn't anticipate would be so messy), it will be more faithful to the nature of the object if you use getters and setters. But I understand that implicit conversion will make some of your code more readable.

Here I try a compromise. I was wondering if I could do the property thing more lightweight, and this is what turned out:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>

class RegisterValue {
protected:
  virtual int convert() const = 0;
  virtual RegisterValue& assign(int) = 0;
public:
  operator int() const { return convert(); }
  RegisterValue& operator= (const RegisterValue& reg_value)
  {
    return assign((int)reg_value);
  }
  RegisterValue& operator= (int value) { return assign(value); }
  RegisterValue& operator+= (int value) { return assign((int)(*this) + value); }
  RegisterValue& operator-= (int value) { return assign((int)(*this) - value); }
  RegisterValue& operator*= (int value) { return assign((int)(*this) * value); }
  RegisterValue& operator/= (int value) { return assign((int)(*this) / value); }
  RegisterValue& operator%= (int value) { return assign((int)(*this) % value); }
  RegisterValue& operator&= (int value) { return assign((int)(*this) & value); }
  RegisterValue& operator|= (int value) { return assign((int)(*this) | value); }
  RegisterValue& operator^= (int value) { return assign((int)(*this) ^ value); }
  RegisterValue& operator>>= (int value) { return assign((int)(*this) << value); }
  RegisterValue& operator<<= (int value) { return assign((int)(*this) >> value); }
};

class Register : private RegisterValue {
public:

  explicit Register(int value) : value_(value) {}

  Register& operator= (const Register& reg)
  {
    value_ = reg.value_;
    return *this;
  }

  RegisterValue& value()
  {
    return *this;
  }

  void print();

private:
  virtual int convert() const;
  virtual RegisterValue& assign(int);
  int value_;
};

void Register::print()
{
  std::cout << "Register::value_ : " << value_ << std::endl;
}

int Register::convert() const
{
  return value_;
}

RegisterValue& Register::assign(int value)
{
  value_ = value;
  return *this;
}

int main(void)
{
  Register x(1);
  x.print();

  Register y(1);
  y.print();

  RegisterValue& i = x.value();

  i = 2;
  x.print();

  i = i + 1;
  x.print();

  i += 1;
  x.print();

  RegisterValue& j = y.value();
  i += j;
  x.print();

  i = j;
  x.print();
}

I may be too late, but still decided I should post it.

Regards

EDIT: Thinking again, this property mechanism consumes one pointer for the vtable, just as the one that uses "parent/owner pointer", which is redundant. At least I hope this is easier to maintain, because you don't have to manage the pointer explicitly.
EDIT 2: Added assignment operator for assigning one RegisterValue to another, because the default one does nothing.

And here is the same thing with implicit conversion, because in the end of the day I start doubting that even the properties are not too much harm for readability:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>

class Register {
public:

  explicit Register(int value) : value_(value) {}

  Register& operator= (const Register& reg)
  {
    value_ = reg.value_;
    return *this;
  }

  operator int() const { return getValue(); }
  Register& operator= (int value) { return setValue(value); }
  Register& operator+= (int value) { return setValue((int)(*this) + value); }
  Register& operator-= (int value) { return setValue((int)(*this) - value); }
  Register& operator*= (int value) { return setValue((int)(*this) * value); }
  Register& operator/= (int value) { return setValue((int)(*this) / value); }
  Register& operator%= (int value) { return setValue((int)(*this) % value); }
  Register& operator&= (int value) { return setValue((int)(*this) & value); }
  Register& operator|= (int value) { return setValue((int)(*this) | value); }
  Register& operator^= (int value) { return setValue((int)(*this) ^ value); }
  Register& operator>>= (int value) { return setValue((int)(*this) << value); }
  Register& operator<<= (int value) { return setValue((int)(*this) >> value); }

  void print();

private:

  int getValue() const { return value_; };

  Register& setValue(int value)
  {
    value_ = value;
    return *this;
  }

  int value_;
};

void Register::print()
{
  std::cout << "Register::value_ : " << value_ << std::endl;
}

int main(void)
{
  Register x(1);
  x.print();

  Register y(1);
  y.print();

  x = 2;
  x.print();

  x = x + 1;
  x.print();

  x += 1;
  x.print();

  x += y;
  x.print();

  x = y;
  x.print();
}
Last edited on
Pages: 12