Eliminated copy requires copy-constructor

K

kjm424

I'm wondering if someone can shed some light on a decision made in the
C++ standard.

It would seem that when passing a temporary into a function that takes
a const reference, the copy constructor must be accessible, even though
the copy can be eliminated.

Could somebody tell me the reason for this? Section 8.5.3 of the final
draft explains the requirement (I think), but with no explanation as to
why.

I have included some example source in case my explanation is lacking.

Thanks for any help.

#include <iostream>

class A
{
public:

A(int i) : m_val(i)
{ }

int val() const
{
return m_val;
}

private:

A(const A&);
A& operator=(const A&);

int m_val;
};

void output(const A& a)
{
std::cerr << "Value: " << a.val() << '\n';
}

int main()
{
A a(3);

output(a); // Works

output(A(5)); // Fails to compile - copy constructor not
accesible

return 0;
}
 
G

Gavin Deane

kjm424 said:
I'm wondering if someone can shed some light on a decision made in the
C++ standard.

It would seem that when passing a temporary into a function that takes
a const reference, the copy constructor must be accessible, even though
the copy can be eliminated.

Could somebody tell me the reason for this? Section 8.5.3 of the final
draft explains the requirement (I think), but with no explanation as to
why.

Think about it logically. By making the copy constructor inaccessible
you are saying that the concept of copying is not applicable to that
type. As you have discovered, the way in which you are using references
requires the concept of copying. For your class, the concept of copying
is not applicable so it makes no sense for your class to be used there
and the compiler quite rightly forbids it.

There is the completely independent point that, *if* the concept of
copying is applicable to a type, there are some situations in which, as
an optimisation, the *act* of copying may be elided. If the language
were to ignore whether the *concept* of copying was applicable in such
cases, then whether your code even compiled or not would depend solely
on the compiler's optimisation settings, which would be mad.

Gavin Deane
 
K

kjm424

Thanks for your response Gavin.
There is the completely independent point that, *if* the concept of
copying is applicable to a type, there are some situations in which, as
an optimisation, the *act* of copying may be elided. If the language
were to ignore whether the *concept* of copying was applicable in such
cases, then whether your code even compiled or not would depend solely
on the compiler's optimisation settings, which would be mad.

This makes sense, optimisation settings shouldn't make any difference
to whether code is deemed correct or not.
Think about it logically. By making the copy constructor inaccessible
you are saying that the concept of copying is not applicable to that
type. As you have discovered, the way in which you are using references
requires the concept of copying. For your class, the concept of copying
is not applicable so it makes no sense for your class to be used there
and the compiler quite rightly forbids it.

I can understand if the type being passed in is different to the
function argument, such as passing an int into a function taking a
const double reference, but when they (the type getting passed in and
the expected type) are both the same type, I fail to see why a copy is
required.

Don't get me wrong, I'm not saying that I think that the compiler or
the standard is wrong, I'm just trying to understand why the way in
which I am using references requires the concept of copying in the
first place.
 
G

Gavin Deane

kjm424 said:
I can understand if the type being passed in is different to the
function argument, such as passing an int into a function taking a
const double reference, but when they (the type getting passed in and
the expected type) are both the same type, I fail to see why a copy is
required.

Don't get me wrong, I'm not saying that I think that the compiler or
the standard is wrong, I'm just trying to understand why the way in
which I am using references requires the concept of copying in the
first place.

Ah, I see. I may have slightly missed the point of your question. So,
are you asking why, in the following program (slightly adapted from
yours), the code marked "Not allowed" requires any copying to
initialise the references?

class A
{
public:
A(int i) : m_val(i) {}
private:
A(const A&);
A& operator=(const A&);
int m_val;
};

void foo(const A& a) {}

int main()
{
// Allowed
A a(3);
foo(a);
const A& r1 = a;

// Not allowed
foo(A(5));
const A& r2 = A(5);
}

If that's your question, then I *think* the answer is in 8.5.3, but
that section is at the limit of my reading standardese. I'm intrigued
enough that I might take a deeper look when I get the time. I believe
the applicable part is this

<quote>
If the initializer expression is an rvalue, with T2 a class type, and
"cv1 T1" is reference compatible with "cv2 T2," the reference
is bound in one of the following ways (the choice is
implementationdefined):

- The reference is bound to the object represented by the rvalue (see
3.10) or to a subobject within that object.
- 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 or to a subobject
within the temporary.
</quote>

which suggests to me that whether the concept of copying is actually
applicable here is implementation defined, which somewhat undermines my
original argument.

But as I say, I'm not fully confident of my understanding of the
standardese here.

Gavin Deane
 
G

Greg

kjm424 said:
Thanks for your response Gavin.


This makes sense, optimisation settings shouldn't make any difference
to whether code is deemed correct or not.


I can understand if the type being passed in is different to the
function argument, such as passing an int into a function taking a
const double reference, but when they (the type getting passed in and
the expected type) are both the same type, I fail to see why a copy is
required.

The copy is needed because the program is passing an rvalue, A(5), as
an argument to a function, output(), that is expecting an object as the
parameter (a const reference, in this case). So in order to provide the
type of parameter that output() is expecting, the compiler must (at
least conceptually) copy the rvalue into an lvalue A object and pass
that object to the output() as a parameter. But with A's copy
constructor inaccessible, the compiler is not allowed to copy the A(5)
temporary so the program cannot be compiled.

Greg
 
V

vijay

kjm424 said:
I'm wondering if someone can shed some light on a decision made in the
C++ standard.

It would seem that when passing a temporary into a function that takes
a const reference, the copy constructor must be accessible, even though
the copy can be eliminated.

Could somebody tell me the reason for this? Section 8.5.3 of the final
draft explains the requirement (I think), but with no explanation as to
why.

I have included some example source in case my explanation is lacking.

Thanks for any help.

#include <iostream>

class A
{
public:

A(int i) : m_val(i)
{ }

int val() const
{
return m_val;
}

private:

A(const A&);
A& operator=(const A&);

int m_val;
};

void output(const A& a)
{
std::cerr << "Value: " << a.val() << '\n';
}

int main()
{
A a(3);

output(a); // Works

output(A(5)); // Fails to compile - copy constructor not
accesible

return 0;
}

output(A(5));
This works perfectly on MSVC++ 6.0 .
 
G

Gavin Deane

vijay said:
output(A(5));
This works perfectly on MSVC++ 6.0 .

I don't think that's saying much. IIRC MSVC++ 6.0 allows this

class A
{
public:
A(int i) {}
};

// Note - non-const
void foo(A& ref) {}

int main()
{
foo(A(42));
}

so it wouldn't surprise me if it got other things wrong in this area
too.

Gavin Deane
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top