Topic: Contradiction in the Standard. Overload resolution between
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Sun, 11 Mar 2012 23:15:51 -0700 (PDT)
Raw View
Am 11.03.2012 09:22, schrieb vlad.moscow@mail.ru:
>
> Early in the C++ Standard there was the phrase ( 12.8.7 ) "7 A member
> function template is never instantiated to perform the copy of a class
> object to an object of its
> class type."
[Let me start with a remark: Please don't quote C++ Standard phrases
by merging sub-clause and paragraph in a single "quotation
coordinate". "12.8.7" should be quoted as "12.8 p(aragraph) 7", for
example]
Note that this particular wording was recognized as a defect and was
fixed for C++11 by
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1080
It was already true in C++03 that copy-constructors participate in
overload resolution in several situations, so this fix is just a
clean-up of an obvious inconsistency in the standard.
> However as it follows from the Standard a template constructor takes
> part in overload resolution even for copy initialization.
Exactly, and this was already true in certain situations in C++03.
C++11 ensured that this is now a much more consistent scheme. See also
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#535
for one of the most recent wording fixes in this regard.
> Let consider an example.
>
> #include<iostream>
>
> struct A
> {
> template<class T>
> A( T& ) { std::cout<< "template<class T> A( T& )\n"; }
> };
>
> void f1( A a ) {}
>
> void f2( A a )
> {
> f1( a );
> }
>
> void f3( const A&a )
> {
> f1( a );
> }
>
> int main()
> {
> int i = 10;
> A a( i );
>
> f2( a );
> f3( a );
>
> return ( 0 );
> }
>
> Here the template constructor is called three times. the first time
> then object a is created. The second and the third time then it passed
> to function f2 and correspondingly to function f1. At least I checked
> this with several compilers.
I think the compiler behaviour is conforming: In these cases the
template constructor is a better match, because the arguments are
non-const. The implicit copy-constructor has the signature
A(const A&)
in this case.
> And the implicit copy constructor is called two times then object a is
> passed to function f3 and correspondingly to function f1, because the
> parameter of f3 declared as const reference.
I count only one call to the copy constructor when f1 is called from f3.
> According to the Standard ( 12.8.1 ) "1 A class object can be copied
> or moved in two ways: by initialization (12.1, 8.5), including for
> function argument
> passing (5.2.2) and for function value return (6.6.3); and by
> assignment (5.17). Conceptually, these two operations are implemented
> by a copy/move constructor (12.1) and copy/move assignment operator
> (13.5.3)."
> Take into account what is writtten: that copying is implemented by
> copy constructor.
> On the other hand there is the folloing phrase in the Standard
> ( 12.8.2 ): " 2 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 (8.3.6). [ Example:
> X::X(const X&) and X::X(X&,int=1) are copy constructors."
>
> Again take into account that the copy constructor is a non-template
> constructor. However in the example I have shown above the template
> consttructor was called for coping initialization.
This is not a contradiction, because there are two different concepts involved:
a) The definition of a copy/move constructor, which is a special
member function and by this role they still have a special meaning in
the language, e.g. when defining trivial types.
b) The functions considered during overload resolution. With this
definition in mind, copy/move constructors are just *candidates*, but
not necessarily the best match per se.
> Let consider another example
>
> #include<iostream>
>
> struct A
> {
> template<class T>
> A( T& ) { std::cout<< "template<class T> A( T& )\n"; }
> private:
> A( const A& ) {}
>
> };
>
> void f1( A a ) {}
>
> void f2( A a )
> {
> f1( a );
> }
>
>
> int main()
> {
> int i = 10;
> A a( i );
>
> f2( a );
>
> return ( 0 );
> }
>
>
> Here again the template constructor is called though the copy
> constructor is explicitly defined as private.
Well, this behaviour is consistent, right? If it were different, we
would have an inconsistency in regard to overload selection rules. The
language is clear, that access checks happen *after* overload
resolution. So given the fact that the template constructor is
selected in preference over the copy/move constructor it would be
astonishing, if the code would not be accepted by the compiler.
> I see here a contradiction and broken semantic of the copy
> constructor. It follows that in fact a template constructor is a copy
> constructor and role of the copy consttructor totally unclear if we
> can do all with a template constructor. So one can say that a copy
> consttructor is in fact any constructor template or non-template which
> takes a reference to object of the same class as a parameter.
The point of misunderstanding is the difference between the term
"copy/move constructor" and the term "constructor selected to
copy/move". The most recent draft N3376 uses the second term when
referring to the effective constructor used for a copy/move operation.
E.g. 3.2 p3 says now:
"A constructor selected to copy or move an object of class type is
odr-used [..]"
But I agree that your mentioned sentence in [class.copy] p1
"Conceptually, these two operations are implemented by a copy/move
constructor (12.1) and copy/move assignment operator (13.5.3)."
is a bit misleading now.
HTH & Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: vlad.moscow@mail.ru
Date: Mon, 12 Mar 2012 09:17:30 -0700 (PDT)
Raw View
On 12 , 10:15, Daniel Kr gler<daniel.krueg...@googlemail.com>
wrote:
Daniel, the problem is that now copy constructor and the copy
assignment operator have diffferent semantic. Let again consider the
original example with some changes.
#include<iostream>
struct A
{
template<class T>
A( const T& )
{ std::cout<< "template<class T> A( const T& )\n"; }
template<class T>
A( T& )
{ std::cout<< "template<class T> A( T& )\n"; }
template<class T>
A& operator =( const T&rhs )
{
std::cout<< "template<class T> operator =( const T& )\n";
return ( *this );
}
template<class T>
A& operator =( T&rhs )
{
std::cout<< "template<class T> operator =( T& )\n";
return ( *this );
}
};
void f1( A a ) {}
void f2( A a )
{
f1( a );
}
void f3( const A&a )
{
f1( a );
}
int main()
{
int i = 10;
A a( i );
f2( a );
f3( a );
A a1( i );
const A a2( i );
a1 = a;
a1 = a2;
return ( 0 );
}
There are two template constructors and two template assignment
operators. The compiler created implicitly copy constructor
A( const A& );
and the copy assignment operator
A& operator =( const A& );
Now let consider their behaviors.
Then function f2 is called and inside it function f1 is called the
template consttructor is invoked. Only then function f1 is called
after call of function f3 the implicitly defined copy constructor is
called.
And what about assignment operators? The template assignment operators
are never called though it seems that the template operator
template<T>
A& operator =( T& );
is more suitable than implicitly defined copy assignment operator. So
we see obvious difference in behavior of the copy constructor and the
copy assignment operator.
In all books on C++ usually the following trick is shown when coping
of object shall be prevented.
struct A
{
template<class T>
A( const T& )
{ std::cout<< "template<class T> A( const T& )\n"; }
template<class T>
A( T& )
{ std::cout<< "template<class T> A( T& )\n"; }
template<class T>
A& operator =( const T&rhs )
{
std::cout<< "template<class T> operator =( const T& )\n";
return ( *this );
}
template<class T>
A& operator =( T&rhs )
{
std::cout<< "template<class T> operator =( T& )\n";
return ( *this );
}
private:
A( const A& );
A& operator =( const A& );
};
Again the see different semantic of this trick, Declaring the copy
assignment operator private we indeed prevent coping of objects then
assigning them each other. But making the copy constructor private
does not prevent coping of objects! They can be copied by using the
template constructor that has as a parameter non-const reference to an
object of type A.
So the semantic of these special function is different. And they
should be described in the Standard separatly, because now they have
not common semantic.
In my opinion the original semantic of coping objects of the same type
described in the old Standard as more correct and consistent while now
it looks like a bug. At least now the statement that we can prevent
coping of objects by making copy constructor and the copy assignment
operator private described in all (!) books on C++ published before
adopting the C++ Standard 2011 are totally incorrect!
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?UTF-8?B?RGFuaWVsIEtyw7xnbGVy?=<daniel.kruegler@googlemail.com>
Date: Tue, 13 Mar 2012 09:07:21 -0700 (PDT)
Raw View
On 2012-03-12 17:17, vlad.moscow@mail.ru wrote:
> On 12 , 10:15, Daniel Kr gler<daniel.krueg...@googlemail.com>
> wrote:
>
> Daniel, the problem is that now copy constructor and the copy
> assignment operator have diffferent semantic.
I think I disagree with that description. See below for details.
> Let again consider the original example with some changes.
>
> #include<iostream>
>
> struct A
> {
> template<class T>
> A( const T& )
> { std::cout<< "template<class T> A( const T& )\n"; }
>
> template<class T>
> A( T& )
> { std::cout<< "template<class T> A( T& )\n"; }
>
> template<class T>
> A& operator =( const T&rhs )
> {
> std::cout<< "template<class T> operator =( const T& )\n";
> return ( *this );
> }
>
> template<class T>
> A& operator =( T&rhs )
> {
> std::cout<< "template<class T> operator =( T& )\n";
> return ( *this );
> }
>
> };
>
> void f1( A a ) {}
>
> void f2( A a )
> {
> f1( a );
> }
>
> void f3( const A&a )
> {
> f1( a );
> }
>
> int main()
> {
> int i = 10;
> A a( i );
>
> f2( a );
> f3( a );
>
> A a1( i );
> const A a2( i );
>
> a1 = a;
> a1 = a2;
>
> return ( 0 );
> }
>
> There are two template constructors and two template assignment
> operators. The compiler created implicitly copy constructor
>
> A( const A& );
>
> and the copy assignment operator
>
> A& operator =( const A& );
>
> Now let consider their behaviors.
>
> Then function f2 is called and inside it function f1 is called the
> template consttructor is invoked.
Yes, I count two calls of the template constructor used for copying,
> Only then function f1 is called
> after call of function f3 the implicitly defined copy constructor is
> called.
Correct.
> And what about assignment operators? The template assignment operators
> are never called though
I think that your code calls first the template assignment operator and
then the copy-assignment operator.
> it seems that the template operator
>
> template<T>
> A& operator =( T& );
>
> is more suitable than implicitly defined copy assignment operator.
Yes, for the first assignment, because the argument is a non-constant
object.
> So
> we see obvious difference in behavior of the copy constructor and the
> copy assignment operator.
I cannot see this difference. The second call taking a const A as
argument should invoke the copy-assignment operator. Maybe you are using
a buggy compiler?
> In all books on C++ usually the following trick is shown when coping
> of object shall be prevented.
>
> struct A
> {
> template<class T>
> A( const T& )
> { std::cout<< "template<class T> A( const T& )\n"; }
>
> template<class T>
> A( T& )
> { std::cout<< "template<class T> A( T& )\n"; }
>
> template<class T>
> A& operator =( const T&rhs )
> {
> std::cout<< "template<class T> operator =( const T& )\n";
> return ( *this );
> }
>
> template<class T>
> A& operator =( T&rhs )
> {
> std::cout<< "template<class T> operator =( T& )\n";
> return ( *this );
> }
>
> private:
> A( const A& );
> A& operator =( const A& );
> };
I'm pretty sure that these books do not suggest to add above template
constructors and/or assignment operators - otherwise I don't see what
the value of that recommendation should be.
> Again the see different semantic of this trick, Declaring the copy
> assignment operator private we indeed prevent coping of objects then
> assigning them each other. But making the copy constructor private
> does not prevent coping of objects! They can be copied by using the
> template constructor that has as a parameter non-const reference to an
> object of type A.
Well, it is already known in C++03 that an unconstrained template
constructor is very "greedy" and would have similar effects in your
program - don't do add such member functions without constraining them
or at least being sure that they do the right thing. Adding such
template members to a non-copyable type is a very bad idea and already
broken in C++03.
> So the semantic of these special function is different. And they
> should be described in the Standard separatly, because now they have
> not common semantic.
Note that already C++03 did consider such templates during overload
resolution, so there is no subtle change. I agree that the current
wording in [class.copy] p1 should be changed to some weaker form, though.
> In my opinion the original semantic of coping objects of the same type
> described in the old Standard as more correct and consistent while now
> it looks like a bug.
I strongly disagree, please refer to my quotes of C++03 where these
template members were already considered during overload resolution.
> At least now the statement that we can prevent
> coping of objects by making copy constructor and the copy assignment
> operator private described in all (!) books on C++ published before
> adopting the C++ Standard 2011 are totally incorrect!
They were already incorrect in C++03, I strongly assume that they did
not consider that someone would add such unconstrained template-members
into their classes.
HTH& Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]