4

I'm working through a textbook of examples about operator overloading and it got me wondering about returning by 'constant value' (for example with operator+). As I understood it, if I returned anything as a const, it was unable to be modified later. Say I have this crude example:

#include <iostream>
using namespace std;

class Temp {
    private:
        int val;
    public:
        Temp(){};

        Temp(int v):val(v){};

        const Temp operator+(const Temp& rhs) const {
            return Temp(this->val + rhs.val);
        }
        int getVal() { return this->val; }
        void setVal(int v) { this->val = v; } 
};

int main() {
    Temp t1, t2, t3;
    t1 = Temp(4);
    t2 = Temp(5);
    t3 = t1 + t2;
    cout << t3.getVal() << endl;
    t3.setVal(100);
    cout << t3.getVal() << endl;
}

After t3 = t1 + t2, t3 is now a const Temp object, not a const Temp&; nevertheless, it is a const. I output its val, then modify it, though I thought I wasnt supposed to be able to do that? Or does that belong only to objects of const Temp&?

pasta_sauce
  • 675
  • 4
  • 16
  • 1
    To those who downvote, can you please explain why? – pasta_sauce Jan 23 '18 at 00:28
  • 3
    _"After `t3 = t1 + t2,` `t3` is now a const Temp object"_ No, it's just been assigned a copy. –  Jan 23 '18 at 00:28
  • @pasta_sauce Learn about copy constructors in c++. That should answer your question. – balki Jan 23 '18 at 00:30
  • 3
    @pasta_sauce _"To those who downvote, can you please explain why?"_ Because you seem to have a basic misconception and should read a [book](https://stackoverflow.com/q/388242) before asking here. –  Jan 23 '18 at 00:30
  • @user9212993 I thought `t3` was assigned a new `Temp` object through use of its constructor? – pasta_sauce Jan 23 '18 at 00:30
  • 2
    @pasta_sauce And how does that make `t3` _magically_ `const` actually? That's just nonsensical. –  Jan 23 '18 at 00:32
  • @user9212993 The `operator+` returns `const Temp`. How does it _not_ return `const` when it is declared as such? – pasta_sauce Jan 23 '18 at 00:34
  • 3
    t3 is default-constructed, and then you modify it using the assignment operator – M.M Jan 23 '18 at 00:34
  • Example where `const` in the return value matters. (Not useful though). https://godbolt.org/g/tzGFZa – balki Jan 23 '18 at 00:37
  • @pasta_sauce You have a non `const` _lvalue_ and assign a copy of the `const` result. That doesn't make the _lvalue_ _automagically_ `const`. –  Jan 23 '18 at 00:38
  • 1
    @balki: The `const` in the return value actually matters here -- it causes `t3 = t1 + t2;` to use copy-assignment instead of move-assignment. – Ben Voigt Jan 23 '18 at 00:45

1 Answers1

7

You say:

t3 is now a const Temp object

After the assignment, but this is where your misunderstanding is.

Once you've declared an object, it will never, never change types.

You've written Temp t1, t2, t3; sot3 is and always will be a non-const Temp object. You're passing a const Temp object (by reference) to its assignment operator, which the operator uses to modify t3.

For a better insight into this, take a look at an assignment operator example.


EDIT: from some of the comments you seem to think that

t3 was assigned a new Temp object through use of its constructor?

Which makes me assume there might be a misunderstanding of constructors here as well. Note that for every object regardless of type, a constructor is called for that object exactly once. This happens when it's first declared. For you, you're calling t3's default constructor when you declare it with Temp t1, t2, t3;.

Furthermore, returning const values from your operators is usually not a good idea. See user4581301's link for more on this: Purpose of returning by const value?

scohe001
  • 13,879
  • 2
  • 28
  • 47
  • 2
    Another good resource is the [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) – user4581301 Jan 23 '18 at 00:38
  • 1
    Might be worth an edit to point out returning by value a constant is not that useful anymore. There's a good question on this. I'll see if I can run it down. Well. That was easy: [Purpose of returning by const value?](https://stackoverflow.com/questions/8716330/purpose-of-returning-by-const-value) – user4581301 Jan 23 '18 at 00:50
  • I think I am tracking. At `t3 = t1 + t2`, it equates to `non-const Temp = const Temp`. That then calls the `operator=`; but if I defined a `const Temp&` return type for the `operator=`, how does a non-cost object get assigned a return type of constant reference? Why even bother returning constant reference to a non-const object? – pasta_sauce Jan 23 '18 at 00:54
  • "*but if I defined a const Temp& return type for the operator=*" it doesn't look like it...If you don't declare an assignment operator, one is declared for you: https://stackoverflow.com/questions/5096464/default-assignment-operator-in-c-is-a-shallow-copy – scohe001 Jan 23 '18 at 00:56
  • @scohe001 yes I understand that, but I ran this code again with a newly declared Big 3 and it still compiled and runs. So if my `operator=` was `const Temp&` return type, then `t3 = t1 + t2`, how does a non-const object get assigned a constant reference? – pasta_sauce Jan 23 '18 at 00:58
  • 1
    @pasta_sauce We're not concerned about the return type of the assignment operator, but the parameter type. Try to change your assignment operator to `Temp& operator=(Temp &t)` (note our parameter is non-const) and this code won't compile. – scohe001 Jan 23 '18 at 00:59
  • @scohe001 I understand that the `operator=` requires a constant reference in the parameter; that constant reference comes from `t1 + t2` as the parameter being passed to the operator by constant reference. When the operator is called, its return type is `const Temp&`, and ends with `return *this` which dereferences the pointer. So doesnt it return a `const Temp&` object and try to assign it to `t3` which is non-const? – pasta_sauce Jan 23 '18 at 01:01
  • @pasta_sauce ahh I see your issue. Normally the assignment operator actually returns a non-const reference, but you can declare one to declare a const reference like that if you'd like. In C++, non-const objects can always be cast to const objects, just not the other way. In the example you give in your comment, `return *this`, `*this` is getting casted to const. – scohe001 Jan 23 '18 at 01:03
  • @pasta_sauce It doesn't *require* a `const` reference. In the context of a parameter `const` is a promise that the parameter will not be modified as a result of calling the function. You can pass in `const`, non `const`, a subclass, and generally have fun while the compiler sorts it out for you. – user4581301 Jan 23 '18 at 01:05
  • @scohe001 So `t3` can take on the values of the returned `const Temp&` but a `const Temp` cannot take on values of a non-cost `Temp` object? Even though the `operator=` may have been defined to return `const Temp&`, since `t3` is non-cosnt, a `const Temp&` object can be cast on it? – pasta_sauce Jan 23 '18 at 01:06
  • @user4581301 I thought it was good practice to define the `operator=` as `const reference`? – pasta_sauce Jan 23 '18 at 01:07
  • @scohe001 Further, when `*this` is called, is that removing the "reference" piece of the `const Temp&`? Thus only returning a `const`? – pasta_sauce Jan 23 '18 at 01:07
  • You say "take on," by which I assume you mean, "be passed through an assignment operator." The ability to "take on" values then would depend on how you've declared the assignment operator. – scohe001 Jan 23 '18 at 01:08
  • @pasta_sauce `*this` in this context is an l-value `Temp` object. With any l-value, you can create references (const or not). – scohe001 Jan 23 '18 at 01:10
  • @scohe001 Yes, so if all the `operator=` was doing was `this->val = rhs.val; return *this;` and its type was `const Temp&`, then `t3` is still a non-const even though it was returned as `const`? – pasta_sauce Jan 23 '18 at 01:10
  • @pasta_sauce please see my answer again. The type of an object will **never ever ever** change over the course of that object's lifetime. You declared `t3` non-const so it will be non-const until it is destroyed. You can cast it to const and return the result of that cast (which it sounds like you do in the assignment operator), but a cast doesn't change the type of the object itself, only the result of an expression. – scohe001 Jan 23 '18 at 01:12
  • My apologies for being unclear. My point is a function that accepts by `const` reference does not have to be provided with a `const` value. the parameter will be `const`-ified and used as though `const` inside the function. As for whether or not you should pass a `const` reference to the assignment operator, sometimes you don't want to. See the [Copy and Swap Idiom](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) for one such time. – user4581301 Jan 23 '18 at 01:18
  • @user4581301 Oh I understand the parameter part about being const or not; its the return part that is getting me a rather bit confused. – pasta_sauce Jan 23 '18 at 01:19
  • @scohe001 _only the result of an expression_; so after all is said and done inside `operator=`, the resulting product is a `const Temp&` object. It doesnt matter however that it is `const Temp&` because we dereference it (with `*this`) and then cast it to `const` but that doesnt do anything to it either? I would that that if I cast something to `const`, it would try to change it to `const`? – pasta_sauce Jan 23 '18 at 01:22
  • In the return you probably DON'T want a `const` reference. The idiomatic operator= looks like `T& operator=(const T& other)`. returning a `const` would be unexpected and break the ability to chain. – user4581301 Jan 23 '18 at 01:22
  • @user4581301 When I went through a course in university, the instructor said it is always best to apply `const T&` to the `operator=`. Just always went with it; when would it be useful? – pasta_sauce Jan 23 '18 at 01:24
  • @pasta_sauce see [this question](https://stackoverflow.com/questions/18669197/c-does-casting-create-a-new-object). Casting will create a temporary new object (in this case a const version of *this, or in other words, a const version of `t3`), but the original is untouched. Does this make sense? – scohe001 Jan 23 '18 at 01:27
  • @scohe001 Yes it does; this is behavior unknown to me and explains a lot more than before. – pasta_sauce Jan 23 '18 at 01:27