?
Solved

Copy constructor or parameterized constructor

Posted on 2006-05-23
18
Medium Priority
?
559 Views
Last Modified: 2012-06-27
Hi all,

I am getting a strange compilaton error.... I am working on g++ 3.4.2..... Following is the scenario.....

I have two independent classes(A and B) and a global function which returns reference to an instance of class A.

class A{
public:
      A()
      {
                cout << "A constructor" << endl;
      }      
};

// Method returns reference to A
A& GetData()
{
      static A obj;
      return obj;
}


class B{
public:
      B()
      {
                cout << "B constructor" << endl;
      }      

     // Parameterized constructor taking reference to object A
      B(const A& refObj)
      {
                cout << "B param constructor" << endl;
      }      
private:
    // Private copy constructor
     B(const B& ref)
      {
                cout << "B copy constructor" << endl;
      }      
};

Now if I create an object(as shown below) of B I am getting an error....

B obj = GetData(); // It should result in call to the parameterized constructor....
// But it gives me an error saying B's copy constructor is private....

So is it a vaild behaviour or is it some sort of bug in gcc?  
0
Comment
Question by:dennis_george
  • 8
  • 5
  • 2
  • +2
18 Comments
 
LVL 5

Author Comment

by:dennis_george
ID: 16740607
I tried to change the private copy constructor into public ... now there is no error.... but it makes a call to the parameterized constructor....

So I don't know if the call is supposed to go to the parameterized constructor then we does it need to check the copy constructor??
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 16740654
Try this:
B obj(GetData());
0
 
LVL 5

Author Comment

by:dennis_george
ID: 16740687
Ya I know that works.... I tried it....

I am here trying to understand this behaviour....
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16740694
>>>> So is it a vaild behaviour or is it some sort of bug in gcc?

It seems to me that it is a bug in gcc. There is no need to check the copy constructor for that statement, but if so it only should give a warning.

Private constructors are a means to prevent instantiation of a class beside of (static) member functions of the class itself. In your case there are two public constructors and one private (copy) constructor what might have the purpose to prevent from making copies. However,  creating two default objects or repeating a parametrized creation would lead to copies as well, though not constructed by copy constructor. Hence, I have difficulties to find a senseful purpose to do it that way.

Regards, Alex


0
 
LVL 12

Assisted Solution

by:rajeev_devin
rajeev_devin earned 400 total points
ID: 16740764
It is a bug in gcc 3.4.2

Check this link for details
http://gcc.gnu.org/ml/gcc-bugs/2005-08/msg01111.html
0
 
LVL 5

Author Comment

by:dennis_george
ID: 16740996
Hi Rajeev,

The bug reported in gcc 3.4.2 was slighlty different than what I am facing..... and more over its not a bug because it is been rejected as invalid...
http://gcc.gnu.org/bugs.html#cxx_rvalbind

Because there they are refering to the copy constructor

class A
{
public:
  A();

private:
  A(const A&);   // private copy ctor
};

A makeA(void);
void foo(const A&);

void bar(void)
{
  foo(A());       // error, copy ctor is not accessible
  foo(makeA());   // error, copy ctor is not accessible

  A a1;
  foo(a1);        // OK, a1 is a lvalue
}

But in my case, I am not at all refering to the copy constructor... so no point of checking with the copy constructor....

I totally agree with Alex... because it is not at all making sense.... more over

B obj(GetData());

and

B obj = GetData();

should behave identically....
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16741160
>>>> Because there they are refering to the copy constructor

Don't think you are right with that. In the second statement

>>>> foo(makeA());  

the return of makeA() was passed "by reference" to foo and the copy constructor was not involved. In the bug description they explicitly tell "even though the copy constructor should never be called" what is exactly the same thing as with you ...

>>>> because it is not at all making sense

No offense intended ;-)

Regards, Alex

0
 
LVL 12

Expert Comment

by:rajeev_devin
ID: 16741588
>> Because there they are refering to the copy constructor
same comments as given by itsmeandnobodyelse

The only thing that I can add is that you didn't read the article properly :-)
0
 
LVL 5

Author Comment

by:dennis_george
ID: 16749312
>> the return of makeA() was passed "by reference" to foo and the copy constructor was not involved.

I think the copy constructor is involved but not called.... please refer to the C++ standards...

[quote]
A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary.

The constructor that would be used to make the copy shall be callable whether or not the copy is actually done.
[/quote]

As I already said the reported bug was rejected because it is very much in accordance with C++ standards....

I think the statement below...
B obj = GetData() ;

is interpreted as
B obj = B(GetData());

And hence the copy constructor is coming in the picture.... but not actually called.... So what do u guys think am i still on the wrong track???

Regards,
Dennis
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16749536
>>>> B obj = GetData() ;
>>>> is interpreted as
>>>> B obj = B(GetData());

No. There is no difference between

   B obj(GetData());

and

   B obj = GetData();

You easily can verify that by adding an assignment operator

   B& B::operator=(const A& a)
   {
         cout << "B::operator=(const A& a) " << endl;
         return *this;
   }

It will not be called for

     B obj = B(GetData());


>>>> a constructor is called to copy the entire rvalue object

Don't think that above C++ doc applies to our case. Look at the original code snippet from the link rajeev had posted:

class x
{
public:
  x() {std::cout << "x() default constructor" << std::endl;}

private:
  x(const x& c) {std::cout << "x(x) copy constructor" << std::endl;}
  friend const x make_x();
};

const x make_x() {return x();}

inline void func(const x& p) {}

int main (int argc, char* argv[])
{
  // The next line compiles with gcc 3.3.1, but not with 3.4.2,
  // when x's copy constructor is private.
  func(make_x());

  return 0;
}

What does "func(make_x());"  ?

1.   return x();

Here the (public) default constructor was created. The allocation happens at the stack cause it is the return argument. You could say it would create a local x object first (a temporary) and then uses the copy constructor to copy the temporary to the stack. However, that is definitively wrong as you easily can verify by checking the outputs in the constructors and the destructor.

2. func(make_x());

Here the stack object created by "return x();"   was passed by reference to func. "By reference" means that only the address was passed. Here is *no* constructor involved, what you could see from the fact that reference arguments and reference members can be declared by forward declaration without need to include the class header.

// a. h
class A
{
public:
     A() { }
     A(const A& a) : m(a.m) {}
private:
     int m;
};

// b.h

// forward
class A;

class  B
{    
     void anotherFunc(A& a);  

     void f( A& a)
     {
           anotherFunc(a);  
     }
};

// c.cpp

#include"b.h"

The c.cpp compiles though it doesn't know from class A. That wouldn't work if any constructor was involved.

Regards, Alex


 

0
 
LVL 5

Author Comment

by:dennis_george
ID: 16750081
>> You easily can verify that by adding an assignment operator
>> 
>>   B& B::operator=(const A& a)
>>   {
>>         cout << "B::operator=(const A& a) " << endl;
>>         return *this;
>>   }

>> It will not be called for

>>     B obj = B(GetData());

Hi Alex....

Can u tell me is there any way in such statement assignment operator will come into picture???  

There is an object being created, so in such scenario constructors (normal, parameterized or copy constructors) are called.... So in this stage how come an assignment operator will play any role???

0
 
LVL 5

Author Comment

by:dennis_george
ID: 16750116
>> could say it would create a local x object first (a temporary) and then uses the copy constructor to copy the temporary to the stack. However, that is definitively wrong as you easily can verify by checking the outputs in the constructors and the destructor.

The link posted by rajeev also contains another link which tells you why the bug was rejected.... quoting from that link...
[quote]
The C++ Standard says that a temporary object should be created in this context and its contents filled with a copy of the object we are trying to bind to the reference; it also says that the temporary copy can be elided, but the semantic constraints (eg. accessibility) of the copy constructor still have to be checked
[/quote]

Thats Y i said copy constructor may not be called but is checked.....
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16750753
>>>> Can u tell me is there any way in such statement assignment operator will come into picture???  

You assumed that "B obj = GetData();" would be compiled as
 
>>>>     B obj = B(GetData());

That implies that either two constructors were called (parameterized constructor at the right side and copy constructor for the assignment) or default constructor (left side), parameterized constructor (right side) and an assignment operator.

Both implications were wrong as you could check by appropriate output statements in the constructors, destructor and operator=.

>>>> The C++ Standard says that a temporary object should be created in this context

I read the changes document you were referring to. You are right, they stated that the *semantics* of the copy constructor need to be checked according C++ Standard. I personally think that this interpretation is weird as it implies differences between the usage of pointers and references which were not appropriate. I wonder whether the following compiles under gcc:

   A* func() { static A a; return &a; }
   ...
   B obj = *func();

or

   A* pa = func();
   A& ra  = *pa;
   B b(ra);

If so, the constraint when using a temporary is of no worth, just to annoy developers.

Regards, Alex
0
 
LVL 5

Author Comment

by:dennis_george
ID: 16751064
Ya the following statement also don't compile in gcc....

   A* func() { static A a; return &a; }
   ...
   B obj = *func();

So do u think the standards are not right? or do you think the compilers were not according to the standard?


0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 300 total points
ID: 16752759
>>>> So do u think the standards are not right?

As we found out ourselves, a private copy constructor doesn't make much sense if there are public constructors as well. If taking that as a fact, all compiler issues were irrelevant, cause you shouldn't do that anyway.

However, if I look at the standard non-pragmatically, I would say, it (the standard) isn't consistent in that point. It applies a constraint - here the private scope of the copy constructor - to a case were the copy constructor definitively was not called. Take the case where you have the choice between three ways where the left shows green, the middle one shows red and the right one has no traffic lights at all. If you go right you wouldn't expect to get a ticket because of the red lights o fthe middle way.

If you consider that public/protected/private doesn't change any machine code but is only a self-restriction to avoid bad code design, a case like that never should give an error (at most a warning). With an error the developer might get forced to make all public - just to please the compiler - what could be a wrong design decision.

Regards, Alex
0
 
LVL 1

Accepted Solution

by:
xf10036 earned 1300 total points
ID: 16773702
Apparently, according to the C++ standard (draft), T x(a) (direct-initialization) and T x = a (copy-initialization) are different when T is a class.  Here's the relevent section of the standard:

    --If the initialization is direct-initialization, or if it is  copy-
      initialization where the cv-unqualified version of the source type
      is the same class as, or a derived class of, the class of the des-
      tination,  constructors  are considered.  The applicable construc-
      tors are enumerated (_over.match.ctor_), and the best one is  cho-
      sen  through  overload resolution (_over.match_).  The constructor
      so selected is called to initialize the object, with the  initial-
      izer expression(s) as its argument(s).  If no constructor applies,
      or the overload resolution is  ambiguous,  the  initialization  is
      ill-formed.

    --Otherwise  (i.e.,  for the remaining copy-initialization cases), a
      temporary is created.  User-defined conversion sequences that  can
      convert  from the source type to the destination type or a derived
      class thereof are enumerated (_over.match.copy_), and the best one
      is  chosen  through overload resolution (_over.match_).  The user-
      defined conversion so selected is called to convert  the  initial-
      izer  expression into a temporary, whose type is the type returned
      by the call of the user-defined conversion function, with the  cv-
      qualifiers  of  the destination type.  If the conversion cannot be
      done or is  ambiguous,  the  initialization  is  ill-formed.   The
      object  being initialized is then direct-initialized from the tem-
      porary according to the rules above.7) In certain cases, an imple-
      mentation is permitted to eliminate the temporary by  initializing
      the object directly; see _class.temporary_.

With regarding to the example given by dennis, since A is not derived from B, the otherwise section apply and the standard specifically stated that a temporary is to be created and in turn used to initialize B using copy-initialization.

>>With an error the developer might get forced to make all public.<<

Only the copy constructor, which is a special kind of animal!

Cheers!

xz
0
 
LVL 5

Author Comment

by:dennis_george
ID: 16796477
Hi xz,

you absolutely right... I think the last paragraph u posted from the standard supports my assumptions...
[Quote]
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that
can convert from the source type to the destination type or (when a conversion function is used) to a
derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through
overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is
ill-formed. The function selected is called with the initializer expression as its argument; if the function
is a constructor, the call initializes a temporary of the destination type. The result of the call
(which is the temporary for the constructor case) is then used to direct-initialize, according to the
rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation
is permitted to eliminate the copying inherent in this direct-initialization by constructing
the intermediate result directly into the object being initialized; see 12.2, 12.8.
[Quote]

So I can say that in copy-initialization. e.g.
B obj = GetData() ;

is interpreted as
B obj = B(GetData());

or to be more precise...
B obj(( B(GetData()) ));

Note:: That double bracket is quite neccessary.... otherwise the meaning changes drastically....

Conclusion: So I think I can conclude that the behavioour I observed was very much in accordance with C++ standards earlier in older compiler it was working because it was not following the standard. But as I upgraded the compiler it showing me the error....

0
 
LVL 1

Expert Comment

by:xf10036
ID: 16801615
Your conclusion is right on!
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses

862 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question