17

(using Visual C++ 2010, compiling in debug with optimizations turned off)

I have the following very simple class:

class exampleClass
{
public:
    exampleClass()
    {
        cout << "in the default ctor" << endl;
    }
private:
    exampleClass (const exampleClass& e)
    {
        cout << "in the copy ctor" << endl;
    }
};

When I try to compile it with the following main:

#include <iostream>
using namespace std;

int main()
{
    exampleClass e1=exampleClass();
    return 0;
}

I get the compilation error:

'exampleClass::exampleClass' : cannot access private
                               member declared in class 'exampleClass'

When I remove the access modifier "private" from the copy ctor, the program compiles and prints only:

in the default ctor

Why is this happening? If the compiler will not invoke the copy ctor anyway, why is it bugging me?

Since some people missed the first line (at least before some edits) i will repeat it:

I compiled in debug with optimizations turned off.

infokiller
  • 3,086
  • 1
  • 21
  • 28
  • 6
    I suspect that the compilation phase **does** use the copy constructor, but the optimization phase (which follows the compilation phase) removes it. So the error is justified. Well, semantically it is anyway - you do use a copy there. But that's just my guess, I don't know how the compiler works. On a side note - why don't you use `exampleClass e1();` instead? It's shorter too. – Vilx- Nov 02 '11 at 11:57
  • Copy elision is a possible optimization, but not a must; the resulting executable must work the same way an executable without that optimization would do. – evnu Nov 02 '11 at 11:59
  • Vlix, I turned off all optimizations (as I mentioned in the beginning, I compiled in debug). Regarding your side note- I was just trying things to understand the compilation of c++ programs better and ended up with this (reduced) error. – infokiller Nov 02 '11 at 13:41
  • 1
    "must work the same way" -- I thought so too, but it's not true for RVO ( http://en.wikipedia.org/wiki/Return_value_optimization ) – Chris Burt-Brown Nov 02 '11 at 13:43

9 Answers9

16

This type of initialization is called copy-initialization. I believe the following clause from the C++11 standard applies here (paragraph 8.5.16, page 204):

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 destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

In this case, the best applicable constructor is the copy ctor, which is private, hence the error message.

To further answer your question, when the copy ctor is private, your program is simply not allowed to pass the complier check because of the rules imposed by the standard. When you make the copy ctor public, the program becomes valid, but the call to the copy ctor is optimized away.

EDIT: Okay, to elaborate on previous paragraph.You're dealing here with the so-called copy elision. While the copy elision is possible in this case, the standard requires you to provide an accessible copy ctor for your class.

12
exampleClass e1=exampleClass();

This will first create a temporary exampleClass using the default constructor and then copy that into e1 using the copy constructor. This will invoke the private copy constructor and thus give you the error. The corrent way to instantiate an instance of a class with the default constructor is this:

exampleClass e1;
orlp
  • 112,504
  • 36
  • 218
  • 315
  • 1
    Read the question carefully: The copy constructor is not called in the OPs case. (I guess it is optimized away). Strictly speaking, this does not answer the question. – Ferdinand Beyer Nov 02 '11 at 11:58
  • 4
    Errors are thrown before optimizing. It does call the copy constructor logically. – orlp Nov 02 '11 at 12:01
  • 3
    -1. `exampleClass e1();` declares a function named `e1` taking no parameters and returning `exampleClass`. The correct way to construct an object with default constructor is: `exampleClass e1;` Though the rest of the answer is correct, so I cancel my -1. – Benoit Nov 02 '11 at 12:01
  • @Benoit: Woops, let me fix that. – orlp Nov 02 '11 at 12:02
  • It answers the question, strictly speaking. In the example given, you can declare e1 by simply writing `exampleClass e1;`. The copy constructor is declared private, among with the assignment operator to define a non-copyable class. See [Non-copyable Mixin](http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-copyable_Mixin) article for an explanation of the non-copyable class concept. – npclaudiu Nov 02 '11 at 12:11
  • nightcracker, note the output of the program: the copy ctor was not invoked. Also, I'm aware of the other ways to invoke the default ctor, but I did this as a learning exercise. – infokiller Nov 02 '11 at 13:57
  • @JohnnyW: Look at this: http://en.wikipedia.org/wiki/Return_value_optimization. The copy constructor does get called logically but is optimized away. As I said, errors are thrown before optimizing. – orlp Nov 04 '11 at 21:29
  • Well, its a bit surprising/confusing that some optimizations are used, although according to the compiler config all optimizations are off. But, this is reality. – infokiller Nov 05 '11 at 21:20
4

The compiler is required to bug you there. While the copy can be elided, the standard requires that the copy constructor is accessible for that type of construction. Of course, you can simplify the code and avoid the copy construction altogether:

exampleClass e1; // Will call exampleClass::exampleClass()
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
1
exampleClass e1=exampleClass();

is the same as:

exampleClass e1(exampleClass());

i.e it invokes the (private) copy constructor.

Ricibob
  • 7,505
  • 5
  • 46
  • 65
1

This is because at compile time, the compiler checks if the function the user is trying to access is really accessible. So when you use exampleClass e1=exampleClass();, it first checks if the copy-constructor is accessible. It spits out an error because the copy-constructor is not private. Remember that at this point the compiler hasn't gone onto the optimization stage where it does the clever stuff as to skip the copy-constructor.

When you make the copy-constructor public, the compiler successfully goes through the stage of parsing the code and making sure that everything is accessible and is in order (there's actually more than that going on) and then at the optimization stage, which usually is on in 'Release' mode it does the clever stuff and by-passes the use of copy-constructor. However if you tried the same code in 'Debug' mode you'd see that the copy-constructor does get called.

Vite Falcon
  • 6,575
  • 3
  • 30
  • 48
  • @downvoter: Care to explain why the down-vote? If you could I would learn something as well rather than you just downvoting just for sake of it! – Vite Falcon Nov 02 '11 at 13:08
1

Everyone explains how you should instantiate an object and @Grigory Javadyan makes a good point on copy elision. It looks like, MSVC does this optimization (so called return value optimization) even in debug mode.

exampleClass e1=exampleClass();

is the same as

exampleClass giveExample()
{
  return exampleClass();
}

exampleClass e1 = giveExample();

You will see that copy ctor will not be called.

But here :

exampleClass giveExample()
{
  exampleClass example;
  return example;
}

exampleClass e1 = giveExample();

you will see another output line :

in the copy ctor

Because you are forcing the compiler to first generate an object and then return it.

Here, here and here some questions I can find, similar to yours.

PS. Link#2 is from another Q&A site. I hope this is not a problem.

Community
  • 1
  • 1
ali_bahoo
  • 4,732
  • 6
  • 41
  • 63
0

Its because the copy constructor is private..

your code is

  • creating a temporary exampleClass and invoking the default constructor exampleClass()
  • attempting to assign the resulting temporary object to e1 using the private copy constructor
StevieG
  • 8,639
  • 23
  • 31
0

That's not how you do instantiate an object in C++. If you want it allocated on the stack, you write:

exampleClass e1;

and you're done, sincee exampleClass' constructor accepts no parameters.

Otherwise, if you want it allocated on the heap, you write:

exampleClass e1 = new exampleClass(); 

The way you wrote it actually creates a temporary object and invokates the copy constructor on that temporary object to create e1. Problem is that your copy-ctor is private, so the compiler's error message.

Simone
  • 11,655
  • 1
  • 30
  • 43
0

when you write

exampleClass e1 = exampleClass() 

it is the same as writing

exampleClass e1( exampleClass() );

which invokes the copy ctor.

AndersK
  • 35,813
  • 6
  • 60
  • 86