I've been experimenting around with the const modifier, trying to make sure I understand it and clearly I do not. I wrote this program. Note line 28, where I am changing the value of the parameter passed to a copy constructor, and the word const on line 40:

#include <iostream>
using namespace std;


class MyClass
{
public:
    int x;

    MyClass (int i)
    {
        x = i;
        cout << "In int constructor. x = " << x << endl;
    }

    MyClass (const MyClass& f)
    {
        cout << "In const copy constructor: f.x = "
             << f.x << endl;
        x = f.x;
    }

    MyClass (MyClass& f)
    {
        cout << "In non-const copy constructor pre-mutation: f.x = "
             << f.x << endl;
        x = f.x;
        f.x = 8;
         cout << "In non-const copy constructor post-mutation: f.x = "
              << f.x << endl;
   }

    ~MyClass ()
    {
        cout << "In destructor: x = " << x << endl;
    }
};


void Func (const MyClass f)
{
    cout << "Inside Func: f.x = " << f.x << endl;
}

int main ()
{
    MyClass f(4);
    cout << "In main before Func call: f.x = " << f.x << endl;
    Func (f);
    cout << "In main after Func call: f.x = " << f.x << endl;
    
    return 0;
}

I have the const modifier on line 40, which I believed made it so f could not be changed and thus I believed would require a call to a copy constructor that couldn't change the parameter passed to it. I wrote a copy constructor that was allowed to change its parameter passed to it and one that wasn't allowed to, using the const keyword. Here's the output.

In int constructor. x = 4
In main before Func call: f.x = 4
In non-const copy constructor pre-mutation: f.x = 4
In non-const copy constructor post-mutation: f.x = 8
Inside Func: f.x = 4
In destructor: x = 4
In main after Func call: f.x = 8
In destructor: x = 8

My question is: why was the non-const copy constructor called rather than the const copy constructor, given the word const on line 40? Thank you.

Recommended Answers

All 11 Replies

This is actually very interesting. Well, first thing, i managed to call const constructor by doing this to main:

int main ()
{
    const MyClass f(4);
    cout << "In main before Func call: f.x = " << f.x << endl;
    Func (f);
    cout << "In main after Func call: f.x = " << f.x << endl;
    cin.get();
    return 0;
}

So, if u pass const object to Func, it will obviously trigger const constructor.

I'll post if i find out something new :)

Ok, I have an idea, but am not sure 100%.

When you write:

void Func (const MyClass g)

You say to compiler: MyClass g (I changed a name from f, but it's irrelevant) will not change!

But, that const keyword is irrelevant of calling constructor.
When you write:

MyClass (const MyClass& f)

You say to compiler: This constructor is going to GET object of type: const MyClass!

When you write inside a main function:

MyClass a(3);
const MyClass b(a);
MyClass c(a);

Func(3); //you are calling int constructor that will create const MyClass g(3)
Func(b); //you are calling const constructor, that will create const MyClass g(b) because b is const
Func(c); //you are calling non-const constructor that will create const MyClass g(c) because c is non-const!

I think you are on to something here, though I'm still puzzled. I ran your revised program and it made the calls to the constructors you predicted it would. Is there something different about calling the copy constructor as opposed to another member function? For example, if I add this member function:

void MyClass::DoSomething (MyClass& f)
{
}

and change my non-member function to this:

void MyClass Func (const MyClass f)
{
     f.x = 10;
     f.DoSomething (f);
}

I get errors on both lines because of the const stipulation, which is what I expected. I have to write function Func in a way that f can't be changed. But it IS changed when the function calls the non-const copy constructor.

MyClass (MyClass& f)
    {
        cout << "In non-const copy constructor pre-mutation: f.x = "
             << f.x << endl;
        x = f.x;
        f.x = 8;
         cout << "In non-const copy constructor post-mutation: f.x = "
              << f.x << endl;
    }

Note that the argument received by the copy constructor is the same as that received by the function DoSomething . The compiler doesn't allow DoSomething to be called because of the lack of the const modifier, but the call to the copy constructor is acceptable. So there must be some difference between a constructor and a regular old member function that allows this, but I don't know what it is.

I don't see why there should be any difference. When you write:

void MyClass Func (const MyClass f)
//or even simply:
const MyClass f(some_MyClass_obj);

And let's say we call Func:

Func(some_other_MyClass_obj);

What happens? First let's see what happens in a function:
1. We put in a Func "some_other_MyClass_obj".
2. Program calls constructor to construct "f", a local object inside Func
3. Constructor is checking what are you giving to him. He is checking if "some_other_MyClass_obj" is of type const MyClass or simply MyClass, and then calls proper constructor function.
He doesn't care about line "const MyClass f", because after the "f" is constructed, it will be given const-ness.
4. f is made, and is given his const-ness.


OK, what happens in your recent functions, I'll try to explain.
You have a member function doSomething. That function takes MyClass as argument, and it can change it.
Of course you get error when you try to change f inside your non-member function, that's because f is const! But the important thing is that the const-ness is given AFTER constructor (it's obvious, because constructor HAS to change object!)
And the only thing that distincts your two constructors (for post before) is that one accepts one type of argument, and the other one accepts other type of argument.

commented: helpful +7

I don't see why there should be any difference. When you write:

void MyClass Func (const MyClass f)
//or even simply:
const MyClass f(some_MyClass_obj);

And let's say we call Func:

Func(some_other_MyClass_obj);

What happens? First let's see what happens in a function:
1. We put in a Func "some_other_MyClass_obj".
2. Program calls constructor to construct "f", a local object inside Func
3. Constructor is checking what are you giving to him. He is checking if "some_other_MyClass_obj" is of type const MyClass or simply MyClass, and then calls proper constructor function.
He doesn't care about line "const MyClass f", because after the "f" is constructed, it will be given const-ness.
4. f is made, and is given his const-ness.


OK, what happens in your recent functions, I'll try to explain.
You have a member function doSomething. That function takes MyClass as argument, and it can change it.
Of course you get error when you try to change f inside your non-member function, that's because f is const! But the important thing is that the const-ness is given AFTER constructor (it's obvious, because constructor HAS to change object!)
And the only thing that distincts your two constructors (for post before) is that one accepts one type of argument, and the other one accepts other type of argument.

OK. This helps, thank you. I don't imagine anyone would ever actually write a class that, one, had two copy constructors, or two, changed the object being passed to it inside of the copy constructor. This was an exercise in trying to understand how const worked. My understanding is that you should always write your copy constructor as such:

public MyClass (const MyClass& f)
{
     // code
}

and that you'd never write this one:

public MyClass (MyClass& f)
{
     // code
}

This question stems from a question I recently took on a test that basically said, "If you don't write a copy constructor, the compiler will implicitly write a copy constructor for you. What will that copy constructor look like?" I think I answered:

public MyClass (const MyClass& f)
{
     // code
}

as opposed to:

public MyClass (MyClass& f)
{
     // code
}

I wasn't positive that was correct and I'm still not, so I started playing around with the program I first listed to try to figure out this question and some other const questions. Does anyone know whether I answered correctly? Sci@phy, thanks for the posts. They helped.

http://groups.google.com/group/comp.lang.c++/browse_thread/thread/16f1dd6dc3cf8fed/1d9e5fea271ef312?lnk=st&q=non-const+copy+constructor+group%3Acomp.lang.c%2B%2B#1d9e5fea271ef312 of any value?

Thanks. Well it shows an example of a const and non-const constructor, so that's relevant. I'm not using any pointers so deep versus shallow copy is not the issue here I don't think. The discussion seems to be efficiency in that thread rather than the ability to change an object, which is my question, and I didn't see anywhere where that is discussed.

OK, I've been researching and the web pages I've visited all seem to say the implicit copy constructor generated by the compiler when you don't create one is:

public MyClass (const MyClass& f)
{
     // code
}

That is, with the const qualifier.

Wikipedia is usually my final arbiter, so I'll go with them, but if anyone actually knows and can confirm this, I'd definitely appreciate it!

Thank you all participants for a very interesting discussion in the thread.
Some additions:
Let's remember what is a copy constructor in C++. Copy constructors are (C++ Std, 12.8):

A 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
...
Note: all forms of copy constructor may be declared for a class.

A compiler-generated (default) copy constructor always has const X& parameter. Some careful compilers print warnings if you have more than one copy constructor declared for a class (VC++ 9, for example, does it). I think, it's a very helpful warning in most of cases.

If a class has a copy constructor with X& parameter, this constructor works for non-const initializer (see the 1st post of the thread). It does not matter what "constantness" of initialized object (see an example with Func parameter binding).

So it's possible to modify a non-const only ARGUMENT object (initializer) via X& copy constructor of another object. Sometimes it's useful feature but as usually is an example of bad practice...

At the same time let's repeate that constantness of ordinar (free, not-member) functions parameters is not a part of function signatures. So it's an error to define two functions:

void F(X x) { ... }
void F(const X x) { ... }

All static member functions are ordinar functions in C++.

this is nothing specific to constructors or passing parameters to functions.
it is just the application of the C++ rules for resolving a call to an overloaded function.

#include <iostream>

void foobar( int& arg )
{ std::cout << "foobar( int& )\n" ; }

void foobar( const int& arg )
{ std::cout << "foobar( const int& )\n" ; }

int main()
{
  int i = 7 ;

  foobar(i) ; // foobar( int& arg ) : exact match
  // foobar( const int& ) : conversion from int& to const int&
  // exact match preferred over conversion

  const int j = 7 ;
  foobar(j) ; // foobar( const int& arg ) : exact match
  // foobar( int& arg ) can't be called at all ( no conversion )

}

in your snippet:

int main ()
{
  MyClass f(4);
  Func (f); // make copy of object f. f is a modifiable lvalue
  // overload resolves to exact match : MyClass::MyClass( MyClass& )
  
  const MyClass& g = f ;
  Func (g); // make copy of object g. g is not a modifiable lvalue
  // call MyClass::MyClass( const MyClass& ) to make the copy
}

the copy constructor that takes a non-const object as argument is used to make copies of non-const objects.
and the copy constructor that takes a const object as argument is used to make copies of const objects.

Okay, thanks guys! That's very helpful. I'm going to mark this one solved.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.