Question about the copy constructor

Hello, recently I've been doing some exercises in an attempt to remember how to overload operators. I've also decided to make a copy constructor
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
  #include <iostream>

using namespace std;
class cents
{
public:
    int m_ncents;

    cents(int cents = 0){ m_ncents = cents;}

//the thing below is suppose to be a copy constructor,
//but the compiler doesn't //want to work and gives an error ( http://pastebin.com/y8s91TFc )
//for some strange reason it works fine when i put "const" in front of the 
//second "cents" (    cents(const cents &cSource)      )
//any ideas why?
    cents(cents &cSource)
    {
        m_ncents = cSource.m_ncents;
    }

};
cents operator+(cents a, cents b)//actually dont ignore this
{
    return cents(a.m_ncents+b.m_ncents);
}

cents operator+(cents a, int b)//ignore
{
    return cents(a.m_ncents+b);
}

cents operator+(int b, cents a)//ignore
{
    return (a+b);
}

ostream& operator<<(ostream& out, cents a) //ignore
{
    out << a.m_ncents;
    return out;
}

int main()
{
    cents a(100);
    cents b(12);
    cents c = 23+a;
    cout << c << endl << c;
    cents d = c; //calling copy constructor, i think
    cout << d;
    d = b;
    cout << d;
}

Thank you in advance :D
Last edited on
closed account (Dy7SLyTq)
try this->m_ncents = cSource.m_ncents
Doesn't work, codeblocks still gives error.
It says that the one of the problems is line 24, where it says
 
" no matching function for call to ‘cents::cents(cents)’|"
Taking a random shot...

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

public:
    int m_ncents;

    cents(int cents = 0){ m_ncents = cents;}

//the thing below is suppose to be a copy constructor,
//but the compiler doesn't //want to work and gives an error ( http://pastebin.com/y8s91TFc )
//for some strange reason it works fine when i put "const" in front of the
//second "cents" (    cents(const cents &cSource)      )
//any ideas why?
    cents(const cents &cSource)
    {
        m_ncents = cSource.m_ncents;
    }
};
cents operator+(cents a, cents b)//ignore
{
    //return cents(a.m_ncents+b.m_ncents); // Error; cannot construct or cast cents during return;
    cents c(a.m_ncents + b.m_ncents);
    return c;
}

cents operator+(cents a, int b)//ignore
{
    cents c(a.m_ncents+b);
    return c;
}

cents operator+(int b, cents a)//ignore
{
    cents c(a.m_ncents+b);
    return c;
}
ostream& operator<<(ostream& out, cents a) //ignore
{
    out << a.m_ncents;
    return out;
}


This edited code compiled on my unit. Not sure if it's doing what you want, but definitely working.

Have fun.
Last edited on
closed account (Dy7SLyTq)
he already said it compiles with const. he wants to know why it doesnt compile without const

edit: i think disch knows a lot about copy constructors so you can message him
Last edited on
Copy ctors are required to have the const before the type, otherwise it is not a copy constructor.
thatotherguy237 wrote:
cents(cents &cSource)

This copy constuctor can only be called with lvalue arguments

thatotherguy237 wrote:
it works fine when i put "const" in front of the
//second "cents" ( cents(const cents &cSource)

This copy constructor can be called with both lvalue and rvalue arguments

The calls to the copy constructor of cents with rvalue arguments are made at the following lines:
1
2
3
4
    return cents(a.m_ncents+b.m_ncents); // cents(...) is rvalue
    return cents(a.m_ncents+b); // cents(...) is rvalue
    return (a+b); // a+b is rvalue
    cents c = 23+a; // 23+a is rvalue 


and the calls with lvalue arguments are made at the following lines:
1
2
3
4
    return (a+b); // copy ctor from a to left argument of operator+
    cents c = 23+a; // copy ctor from a to right argument of operator+
    cout << c << endl << c; // copy ctor from c to right arg of operator<<
    cents d = c; // the obvious one 


as for what lvalues and rvalues are.. try http://en.cppreference.com/w/cpp/language/value_category

and for which references can be initialized with which expressions, http://en.cppreference.com/w/cpp/language/reference_initialization

PS:
firedraco wrote:
Copy ctors are required to have the const before the type

not true: copy constructors are defined as follows by 12.8[class.copy]/2: "non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments"
Last edited on
Was debugging his errors from his pastie, inserted in his comments:
 
//but the compiler doesn't //want to work and gives an error ( http://pastebin.com/y8s91TFc ) 


As for the copy constructor, never really thought about why you must use const. But that's how it goes.

Reference:
"The function fun() returns by value. So the compiler creates a temporary object which is copied to t2 using copy constructor in the original program (The temporary object is passed as an argument to copy constructor). The reason for compiler error is, compiler created temporary objects cannot be bound to non-const references and the original program tries to do that. It doesn’t make sense to modify compiler created temporary objects as they can die any moment."

I wouldn't be able to explain it myself, but I do question the "can die any moment". Any moment is vague.

http://www.geeksforgeeks.org/copy-constructor-argument-const/
closed account (Dy7SLyTq)
how is that vague? if it can die at any "random" time then you cant specify it much more than at any moment, short of listing every single possible way and reason it can die
In behavior:
"The reason for compiler error is, compiler created temporary objects cannot be bound to non-const references"

But why can it not be bound to non-const references?(you can't because you can't - I follow the rules so I understand, though I still question)

Now the logic to why it must be const (good article btw):
"It doesn’t make sense to modify compiler created temporary objects as they can die any moment"

I can understand why it shouldn't be modifiable ( because it was created during compilation time) but the last phrase "can die any moment" has got me pondering.

What is the life of the actually temporary object? Shouldn't it be when it's no long used? Had me thinking too much of JAVA's garbage collector.

Joke:
"We do it this way, because this is how it's done... okay.. okay fine...
Also don't touch that, it can go out any minute. What? When? Why?"

Just a nagging left-over taste - obey the functionality of the language, it'll make things easier.
Nothing to see here.

Hopefully OP's question was resolved.
Last edited on
@DPut lifetimes of the temporaries are well defined in C++, but to keep this focused, the original reason (from D&E) why a function taking non-const lvalue reference cannon be called with rvalue expression is because the caller of such function will never be able to observe the modifications made by the function. (And if the function does not intend to modify its by-reference argument, it shouldn't declare it non-const.
Op here, just read your replies, and now my head hurts.
So the original program didn't compile because tempory objects can't be bind to a non const reference?
Alrighty then....
Sorry if late, but can someone explain, in a idiot-proof way, why a const copy constructor can be called with both lvalue and rvalue arguments while a non-const copy constructor can be called with only lvalues?

Well, according to Cubbi anyways.

(and i would like to know where u learn tis stuff)
---Something I wanted to mention---
I am not trying to increase the confusion here, just wanted to mention.
According to Microsoft:

Temporary Objects Cannot be Bound to Non-Const References

In previous releases of Visual C++, non-const references could be bound to temporary objects. Now, temporary objects can only be bound to const references.

The above was in reference to Microsoft Visual Studios 2008.
http://msdn.microsoft.com/en-us/library/cfbk5ddc(v=vs.90).aspx

So some older compilers, possibly not just Microsoft, may allow usage of non-const reference(./?)
Therefore, design of copy constructors without const were valid with those compilers.
My only assumption is that the compiler would elide the copy constructor call.


---As for what op stated in the previous post---

"explain '...' why a const copy constructor can be called with both lvalue and rvalue arguments while a non-const copy constructor can be called with only lvalues"

Cubbi mentioned it as a logic-design statement (I believe).
First let's try to understand lvalue and rvalue (skip if you already know and have a better definition than mines)
lvalue == non-temporary data, that's all it means. (Has more meaning than that but just think of it that way)
Example: string s;
rvalue == temporary data, same comment as above.
Example: "this is my string"
http://en.cppreference.com/w/cpp/language/value_category

So, using functions called with string data (not objects, for an easier understanding):

With const, you could pass (by reference) a string variable or a "this is a string" temporary variable (aka lvalue and rvalue)

Without const, you could pass (by reference) a string variable, but not a "this is a string" temporary variable because omitting const allows you to change the value and address - which isn't really applicable to "this is a string". (The compiler would stop you - hence why it is only possible of lvalue).

The same applies to objects.
const reference:
cents(myCents)
cents(Cents(/*some_data*/)) //casting data to object Cents

without const, with reference:
cents(myCents)
//cents(Cents(/*some_data*/)) - won't compile, casting data to object Cents

That's what I believe Cubbi was stating.
Last edited on
What I was saying is that if the ctor was to modify the rvalue, nobody would know. Like a tree falling in a forest...

@Dput "this is my string" is an lvalue expression (as the link you cited says) although in most situations it does turn into the rvalue pointer, which is indeed very pointless to attempt to modify.

Most common rvalues are value-returning function/operator calls. For example a+b is an rvalue because operator+ returns by value.

Suppose a and b have type Foo, and now you call a ctor of class Foo that takes that sum.

Foo x = Foo(a+b);

Now imagine rvalues bind to non-const refs. Suppose your ctor was
Foo::Foo(Foo& arg) { arg = some_new_value; }

You just lost that some_new_value. There is no syntax in the language that would allow the code that executed Foo(a+b) to become aware of it what you did to the result of a+b. That's why it was decided (back in the 80s) to disallow this binding, just how C++ disallows modifications through pointers to const (which was a 1985 C++ invention, C adopted const by 1989)

Of course 20 years later people realized that there is a very good reason to modify rvalues in some situations and that brought about rvalue references, but save it for later.
closed account (Dy7SLyTq)
The above was in reference to Microsoft Visual Studios 2008.
msvs is an ide... i think you mean msvc++
Topic archived. No new replies allowed.