Discussion:
template copy constructor
(too old to reply)
Vladimir Grigoriev
2009-10-07 10:55:29 UTC
Permalink
Let consider the code

template <typename T>
class A
{
public:
A( T value = 0 ){}
A( A<T> &rhs ){} // without const attribute
template <typename U>
A( A<U> &rhs ){} // without const attribute
};

main()
{
A<int> a1;
A<int> a2 = a1; // this is O'k
A<double> a3( a1 ); // this is O'k because A( A<U> &rhs ) is
called
A<double> a4 = a1; // here is a compilation array
}

What should I add (maybe some operato function) rthat the last statement
will be compiled? I.e. what is the appropriate sequence of type conversions
that is needed to compile the code taking into account that the template
constructor with const reference should not be used?



Vladimir Grigoriev
David Wilkinson
2009-10-07 11:25:27 UTC
Permalink
Post by Vladimir Grigoriev
Let consider the code
template <typename T>
class A
{
A( T value = 0 ){}
A( A<T> &rhs ){} // without const attribute
template <typename U>
A( A<U> &rhs ){} // without const attribute
};
main()
{
A<int> a1;
A<int> a2 = a1; // this is O'k
A<double> a3( a1 ); // this is O'k because A( A<U> &rhs ) is
called
A<double> a4 = a1; // here is a compilation array
}
What should I add (maybe some operato function) rthat the last statement
will be compiled? I.e. what is the appropriate sequence of type conversions
that is needed to compile the code taking into account that the template
constructor with const reference should not be used?
Vladimir:

Code compiles for me on VS2008 (if I correct to int main()).
--
David Wilkinson
Visual C++ MVP
Vladimir Grigoriev
2009-10-07 11:35:55 UTC
Permalink
I am sorry. I did not mention that I use Visual C++ 2005 EE.
This compiler does not consider
template <typename U>
A( A<U> &rhs ){}

as a template copy constructor. It considers the function as an usual
constructor with one parameter.

I tried the same operation with std:auto_ptr and I got the same compilation
error

The statement
std::auto_ptr<Y> py2( new Y );

std::auto_ptr<X> px2 = py2;


------ Build started: Project: auto_ptr_design1, Configuration: Debug
Win32 ------

Compiling...

auto_ptr_design1.cpp

c:\documents and settings\vgrigoryev\my documents\visual studio
2005\projects\auto_ptr_design1\auto_ptr_design1.cpp(214) : error C2440:
'initializing' : cannot convert from 'std::auto_ptr<_Ty>' to
'std::auto_ptr<_Ty>'

with

[

_Ty=Y

]

and

[

_Ty=X

]

No user-defined-conversion operator available that can perform this
conversion, or the operator cannot be called

Build log was saved at "file://c:\Documents and Settings\vgrigoryev\My
Documents\Visual Studio 2005\Projects\auto_ptr_design1\Debug\BuildLog.htm"

auto_ptr_design1 - 1 error(s), 0 warning(s)

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Vladimir Grigoriev
Post by David Wilkinson
Post by Vladimir Grigoriev
Let consider the code
template <typename T>
class A
{
A( T value = 0 ){}
A( A<T> &rhs ){} // without const attribute
template <typename U>
A( A<U> &rhs ){} // without const attribute
};
main()
{
A<int> a1;
A<int> a2 = a1; // this is O'k
A<double> a3( a1 ); // this is O'k because A( A<U> &rhs ) is
called
A<double> a4 = a1; // here is a compilation array
}
What should I add (maybe some operato function) rthat the last statement
will be compiled? I.e. what is the appropriate sequence of type
conversions that is needed to compile the code taking into account that
the template constructor with const reference should not be used?
Code compiles for me on VS2008 (if I correct to int main()).
--
David Wilkinson
Visual C++ MVP
Vladimir Grigoriev
2009-10-07 12:13:40 UTC
Permalink
It seems that the template copy constructor that is written usually as

template <typename U>
auto_ptr( auto_ptr<U> &rhs ) throw(): ptr( rhs.release() )
{
}

should be wtitten as

template <typename U>
auto_ptr( auto_ptr<U> &rhs ) throw(): ptr( ( U * ) rhs.release() )
{
}

Vladimir Grigoriev
Igor Tandetnik
2009-10-07 12:52:31 UTC
Permalink
Post by Vladimir Grigoriev
It seems that the template copy constructor that is written usually as
template <typename U>
auto_ptr( auto_ptr<U> &rhs ) throw(): ptr( rhs.release() )
{
}
should be wtitten as
template <typename U>
auto_ptr( auto_ptr<U> &rhs ) throw(): ptr( ( U * ) rhs.release() )
{
}
No, it's intentionally written the way it is. The idea is that a
constructor from a "wrong" type should only compile if U* is implicitly
convertible to T*. You don't want to end up with auto_ptr<int> holding a
pointer to double, but you do want to enable something like this:

class B {};
class D : public B {};

auto_ptr<D> pd(new D);
auto_ptr<B> pb(pd);
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
David Wilkinson
2009-10-07 12:26:59 UTC
Permalink
Post by Vladimir Grigoriev
I am sorry. I did not mention that I use Visual C++ 2005 EE.
This compiler does not consider
template <typename U>
A( A<U> &rhs ){}
as a template copy constructor. It considers the function as an usual
constructor with one parameter.
Vladimir:

Yes, your code does not compile on Comeau:

"ComeauTest.c", line 16: error: class "A<double>" has no suitable copy constructor
A<double> a4 = a1;
^

Interestingly, Comeau will compile if either of your constructors uses const
reference argument.

As I understand it, in the above, the compiler should first construct a
temporary A<double> object from a1, and then use the A<double> copy constructor.
This requires the copy constructor to take a const reference argument, because
otherwise you are binding a non-const reference to a temporary.

So it is clear why it works if you make the explicit copy constructor take a
const reference argument. It seems that Comeau regards making the second
(template) constructor take a const reference argument to define a copy
constructor with const reference argument (by setting U = T). I'm not sure if
this is right, but if Comeau says it is, then it probably is :-).

It is odd that VS2008 will compile your code, but VS2005 will not. According to
Comeau, this is a regression.
--
David Wilkinson
Visual C++ MVP
Vladimir Grigoriev
2009-10-07 12:34:41 UTC
Permalink
David, I think that maybe the code is compiled with VS2008 because you have
not disabled Language Extensions for your project as Victor Bazarov pointed
out, have you?

Vladimir Grigoriev
Post by David Wilkinson
Post by Vladimir Grigoriev
I am sorry. I did not mention that I use Visual C++ 2005 EE.
This compiler does not consider
template <typename U>
A( A<U> &rhs ){}
as a template copy constructor. It considers the function as an usual
constructor with one parameter.
"ComeauTest.c", line 16: error: class "A<double>" has no suitable copy constructor
A<double> a4 = a1;
^
Interestingly, Comeau will compile if either of your constructors uses
const reference argument.
As I understand it, in the above, the compiler should first construct a
temporary A<double> object from a1, and then use the A<double> copy
constructor. This requires the copy constructor to take a const reference
argument, because otherwise you are binding a non-const reference to a
temporary.
So it is clear why it works if you make the explicit copy constructor take
a const reference argument. It seems that Comeau regards making the second
(template) constructor take a const reference argument to define a copy
constructor with const reference argument (by setting U = T). I'm not sure
if this is right, but if Comeau says it is, then it probably is :-).
It is odd that VS2008 will compile your code, but VS2005 will not.
According to Comeau, this is a regression.
--
David Wilkinson
Visual C++ MVP
David Wilkinson
2009-10-07 15:11:17 UTC
Permalink
Post by Vladimir Grigoriev
David, I think that maybe the code is compiled with VS2008 because you have
not disabled Language Extensions for your project as Victor Bazarov pointed
out, have you?
Ah yes, sorry about that.
--
David Wilkinson
Visual C++ MVP
Igor Tandetnik
2009-10-07 12:48:29 UTC
Permalink
Post by Vladimir Grigoriev
I am sorry. I did not mention that I use Visual C++ 2005 EE.
This compiler does not consider
template <typename U>
A( A<U> &rhs ){}
as a template copy constructor.
There ain't no such thing as a template copy constructor:

12.8p2 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).106)
Footnote 106: Because a template constructor is never a copy
constructor, the presence of such a template does not suppress the
implicit declaration of a copy constructor. Template constructors
participate in overload resolution with other constructors, including
copy constructors, and a template constructor may be used to copy an
object if it provides a better match than other constructors.

However, whether this constructor is a copy constructor is immaterial -
copy constructors are in no way special for purposes of initialization.
Templates are also a red herring here - the same effect can be
demonstrated without:

struct B {};

struct A {
A(A&) {}
A(B&) {}
};

int main() {
B b;
A a1(b); // OK
A a2 = b; // error
return 0;
}

The second initialization fails for reasons Victor Bazarov explained.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
Vladimir Grigoriev
2009-10-07 14:51:01 UTC
Permalink
Well, I have started from the very beginning. I have written the auto_ptr
without template functions, and it works.

template <typename T>

class odd_ptr

{

private:

template <typename U>

struct odd_ptr_ref

{

odd_ptr_ref( odd_ptr<U> &rhs ): r( rhs ){}

odd_ptr<U> &r;

};

public:

typedef T element_type;

explicit odd_ptr( T *p = 0 ) throw(): ptr( p ){}

odd_ptr( odd_ptr<T> &rhs ) throw(): ptr( rhs.release() ){}

odd_ptr( odd_ptr_ref<T> rhs ) throw(): ptr( rhs.r.release() ){}

~odd_ptr() { delete ptr; }

odd_ptr & operator=( odd_ptr<T> & rhs ) throw()

{

reset( rhs.release() );

return ( *this );

}

odd_ptr & operator=( odd_ptr_ref<T> rhs ) throw()

{

reset( rhs.r.release() );

return ( *this );

}

operator odd_ptr_ref<T>() throw()

{

return ( odd_ptr_ref<T>( *this ) );

}

T & operator*() const throw()

{

return ( *ptr );

}

T * operator->() const throw()

{

return ( return ( ptr ) );

}

T * get() const throw()

{

return ( ptr );

}

T * release() throw()

{

T *p = ptr;

ptr = 0;

return ( p );

}

void reset( T *p = 0 )

{

if ( ptr != p )

{

delete ptr;

ptr = p;

}

}

private:

T *ptr;

};

For constructions as the following

odd_ptr<int> pi1( new int( 10 ) );

odd_ptr<int>pi2( pi1 );

the copy constructor is called.

For constructions as the following

odd_ptr<int> pi1( new int( 10 ) );

odd_ptr<int>pi2 = pi1;

also the copy constructor is called, i.e. the compiler skips the step of
generating odd_ptr<int>( pi1 ).

For constructions as the following

template <typename T>

odd_ptr<T> f()

{

return ( odd_ptr<T>( new T( 0 ) ) );

}

odd_ptr<int> pi1 = f<int>();

at first operator odd_ptr_ref<T>() throw() is called and then odd_ptr(
odd_ptr_ref<T> rhs ) throw(): ptr( rhs.r.release() ){} is called.

However looking through some articles about auto_ptr I do not find sometimes
such operator as

odd_ptr & operator=( odd_ptr_ref<T> rhs ) throw()

{

reset( rhs.r.release() );

return ( *this );

}

which is needed in my code for executing

odd_ptr<int> pi1;

pi1 = f<int>();

Is set of constructors and operators for auto_ptr predefined in C++
standard? Does the above assignment operator exist in the standard set of
functions for auto_ptr or other conversion sequence is used for the
statement pi1 = f<int>()?



Vladimir Grigoriev
Vladimir Grigoriev
2009-10-07 15:04:09 UTC
Permalink
I have tried the code below with std::auto_ptr

std::auto_ptr<int> api;

api = f<int>();

and I have gotten the error

------ Build started: Project: auto_ptr_build, Configuration: Debug
Win32 ------

Compiling...

auto_ptr_build.cpp

c:\documents and settings\vgrigoryev\my documents\visual studio
2005\projects\auto_ptr_build\auto_ptr_build.cpp(117) : error C2679: binary
'=' : no operator found which takes a right-hand operand of type
'odd_ptr<T>' (or there is no acceptable conversion)

with

[

T=int

]

c:\program files\microsoft visual studio 8\vc\include\memory(587): could be
'std::auto_ptr<_Ty> &std::auto_ptr<_Ty>::operator =(std::auto_ptr<_Ty> &)
throw()'

with

[

_Ty=int

]

c:\program files\microsoft visual studio 8\vc\include\memory(593): or
'std::auto_ptr<_Ty> &std::auto_ptr<_Ty>::operator =(std::auto_ptr_ref<_Ty>)
throw()'

with

[

_Ty=int

]

while trying to match the argument list '(std::auto_ptr<_Ty>, odd_ptr<T>)'

with

[

_Ty=int

]

and

[

T=int

]

Build log was saved at "file://c:\Documents and Settings\vgrigoryev\My
Documents\Visual Studio 2005\Projects\auto_ptr_build\Debug\BuildLog.htm"

auto_ptr_build - 1 error(s), 0 warning(s)

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Does it mean that inside the standard auto_ptr there is no such operator as
I used??

odd_ptr & operator=( odd_ptr_ref<T> rhs ) throw()

{

reset( rhs.r.release() );

return ( *this );

}



Vladimir Grigoriev
Ulrich Eckhardt
2009-10-07 15:33:33 UTC
Permalink
error C2679: binary '=' : no operator found which takes a
right-hand operand of type 'odd_ptr<T>' (or there is no
acceptable conversion)
with
[
T=int
]
c:\program files\microsoft visual studio 8\vc\include\memory(587): could
be 'std::auto_ptr<_Ty> &std::auto_ptr<_Ty>::operator =(std::auto_ptr<_Ty>
&) throw()'
You're mixing std::auto_ptr and odd_ptr.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2009-10-07 15:54:46 UTC
Permalink
Yes, Ulrich, you are right. By mistake I forgot that my function f() is
written for odd_ptr!:)
Post by Ulrich Eckhardt
error C2679: binary '=' : no operator found which takes a
right-hand operand of type 'odd_ptr<T>' (or there is no
acceptable conversion)
with
[
T=int
]
c:\program files\microsoft visual studio 8\vc\include\memory(587): could
be 'std::auto_ptr<_Ty> &std::auto_ptr<_Ty>::operator =(std::auto_ptr<_Ty>
&) throw()'
You're mixing std::auto_ptr and odd_ptr.
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2009-10-07 15:50:45 UTC
Permalink
Oh, sorry. I used incorrect function f() defined for odd_ptr with
std::auto_ptr!:)
Post by Vladimir Grigoriev
I have tried the code below with std::auto_ptr
std::auto_ptr<int> api;
api = f<int>();
and I have gotten the error
------ Build started: Project: auto_ptr_build, Configuration: Debug
Win32 ------
Compiling...
auto_ptr_build.cpp
c:\documents and settings\vgrigoryev\my documents\visual studio
2005\projects\auto_ptr_build\auto_ptr_build.cpp(117) : error C2679: binary
'=' : no operator found which takes a right-hand operand of type
'odd_ptr<T>' (or there is no acceptable conversion)
with
[
T=int
]
c:\program files\microsoft visual studio 8\vc\include\memory(587): could
be 'std::auto_ptr<_Ty> &std::auto_ptr<_Ty>::operator =(std::auto_ptr<_Ty>
&) throw()'
with
[
_Ty=int
]
c:\program files\microsoft visual studio 8\vc\include\memory(593): or
'std::auto_ptr<_Ty> &std::auto_ptr<_Ty>::operator
=(std::auto_ptr_ref<_Ty>) throw()'
with
[
_Ty=int
]
while trying to match the argument list '(std::auto_ptr<_Ty>, odd_ptr<T>)'
with
[
_Ty=int
]
and
[
T=int
]
Build log was saved at "file://c:\Documents and Settings\vgrigoryev\My
Documents\Visual Studio 2005\Projects\auto_ptr_build\Debug\BuildLog.htm"
auto_ptr_build - 1 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Does it mean that inside the standard auto_ptr there is no such operator
as I used??
odd_ptr & operator=( odd_ptr_ref<T> rhs ) throw()
{
reset( rhs.r.release() );
return ( *this );
}
Vladimir Grigoriev
Ulrich Eckhardt
2009-10-07 15:31:47 UTC
Permalink
Vladimir Grigoriev wrote:
[auto_ptr-like odd_ptr class]
Post by Vladimir Grigoriev
However looking through some articles about auto_ptr I do not find
sometimes such operator as
odd_ptr & operator=( odd_ptr_ref<T> rhs ) throw()
{
reset( rhs.r.release() );
return ( *this );
}
which is needed in my code for executing
odd_ptr<int> pi1;
pi1 = f<int>();
Is set of constructors and operators for auto_ptr predefined in C++
standard?
Yes. Quoting from 20.4.5:

// 20.4.5.1 construct/copy/destroy:
explicit auto_ptr(X* p =0) throw();
auto_ptr(auto_ptr&) throw();
template<class Y> auto_ptr(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr&) throw();
template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr_ref<X> r) throw();
˜auto_ptr() throw();
// 20.4.5.2 members:
X& operator*() const throw();
X* operator->() const throw();
X* get() const throw();
X* release() throw();
void reset(X* p =0) throw();
// 20.4.5.3 conversions:
auto_ptr(auto_ptr_ref<X>) throw();
template<class Y> operator auto_ptr_ref<Y>() throw();
template<class Y> operator auto_ptr<Y>() throw();


Notes:
- Your destructor isn't declared throw().
- auto_ptr_ref is defined in the surrounding namespace, not nested in class
auto_ptr. In any case, the whole name is odd_ptr::odd_ptr_ref in your case,
with a redundant occurrence of 'odd_ptr'.
- Your odd_ptr_ref is a template, but it is only ever used with the same
template parameter as the surrounding auto_ptr, i.e. the T and U are always
the same. You should be able to drop the template parameter on the
reference type.
- return is not a function, hence no brackets are necessary. While it
doesn't hurt to add brackets around an expression, adding them
unnecessarily IMHO only helps confuse people by making it harder to read.
- reset() checks if the assigned pointer is the same as the one stored.
Since auto_ptr should ensure exclusive ownership, this could only happen in
the case that the pointers are both null. Otherwise, this would be a
programming error. You should rather use assert() than guard against this
symptom of abuse. The only problem I see there is self-assignment.


BTW: There is a glitch in std::auto_ptr. It should not return the pointer
from its release() function. The reason is simple: If you store it e.g. in
a container that assumes ownership, you would probably do this:

cont.push_back(ptr.release());

However, if the push_back() throws e.g. a bad_alloc, you have already
released ownership without anyone taking it. For that, make it not return
anything in order to enforce this order:

cont.push_back(ptr.get());
ptr.release();

A standard-conformant auto_ptr implementation must have this glitch though,
I just wanted to point that out since you're studying this thing.


Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Igor Tandetnik
2009-10-07 15:53:35 UTC
Permalink
Post by Vladimir Grigoriev
Well, I have started from the very beginning. I have written the
auto_ptr without template functions, and it works.
That's because you don't have any constructors from "wrong" odd_ptr
type.
Post by Vladimir Grigoriev
For constructions as the following
odd_ptr<int> pi1( new int( 10 ) );
odd_ptr<int>pi2 = pi1;
also the copy constructor is called, i.e. the compiler skips the step
of generating odd_ptr<int>( pi1 ).
odd_ptr<int>pi2 = pi1;

is equivalent to

odd_ptr<int>pi2(pi1);

because both are of the same type. From C++98 standard:

8.5p14
- 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... [long description of direct initialization snipped]
- Otherwise (i.e., for the remaining copy-initialization cases)... [long
description of copy initialization snipped]

So, you need constructors involving unrelated types in order to
reproduce the issue.
Post by Vladimir Grigoriev
However looking through some articles about auto_ptr I do not find
sometimes such operator as
odd_ptr & operator=( odd_ptr_ref<T> rhs ) throw()
{
reset( rhs.r.release() );
return ( *this );
}
There should be one. See DR127:

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#127
Post by Vladimir Grigoriev
Is set of constructors and operators for auto_ptr predefined in C++
standard?
Yes. From C++98 20.4.5:

namespace std {
template<class X> class auto_ptr {
template <class Y> struct auto_ptr_ref {};
public:
typedef X element_type;

// 20.4.5.1 construct/copy/destroy:
explicit auto_ptr(X* p =0) throw();
auto_ptr(auto_ptr&) throw();
template<class Y> auto_ptr(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr&) throw();
template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
~auto_ptr() throw();

// 20.4.5.2 members:
X& operator*() const throw();
X* operator->() const throw();
X* get() const throw();
X* release() throw();
void reset(X* p =0) throw();

// 20.4.5.3 conversions:
auto_ptr(auto_ptr_ref<X>) throw();
template<class Y> operator auto_ptr_ref<Y>() throw();
template<class Y> operator auto_ptr<Y>() throw();
};
}

From the draft C++0x D.9:

namespace std {
template <class Y> struct auto_ptr_ref { };

template <class X> class auto_ptr {
public:
typedef X element_type;

// D.9.1.1 construct/copy/destroy:
explicit auto_ptr(X* p =0) throw();
auto_ptr(auto_ptr&) throw();
template<class Y> auto_ptr(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr&) throw();
template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr_ref<X> r) throw();
~auto_ptr() throw();

// D.9.1.2 members:
X& operator*() const throw();
X* operator->() const throw();
X* get() const throw();
X* release() throw();
void reset(X* p =0) throw();

// D.9.1.3 conversions:
auto_ptr(auto_ptr_ref<X>) throw();
template<class Y> operator auto_ptr_ref<Y>() throw();
template<class Y> operator auto_ptr<Y>() throw();
};

template <> class auto_ptr<void>
{
public:
typedef void element_type;
};
}

The differences are mostly in response to DR127.
Post by Vladimir Grigoriev
Does the above assignment operator exist in the standard
set of functions for auto_ptr
Not in C++98, but that's considered a defect. Yes in C++0x.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
Vladimir Grigoriev
2009-10-07 16:05:59 UTC
Permalink
Post by Igor Tandetnik
Post by Vladimir Grigoriev
However looking through some articles about auto_ptr I do not find
sometimes such operator as
odd_ptr & operator=( odd_ptr_ref<T> rhs ) throw()
{
reset( rhs.r.release() );
return ( *this );
}
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#127
In the russion edition of "The C++ Standard Template Library" by P.J.
Planger, A.A. Stepanov, D.R. Musser in the listing of memory.h I did not
find this operator. So I asked the question.

Vladimir Grigoriev
Vladimir Grigoriev
2009-10-08 12:58:59 UTC
Permalink
Now I have included some template operators
The definitions looks the following way

template <typename T>

class odd_ptr

{

private:

template <typename U>

struct odd_ptr_ref

{

odd_ptr_ref( odd_ptr<U> &rhs ): r( rhs ){}

odd_ptr<U> &r;

};

public:

typedef T element_type;

explicit odd_ptr( T *p = 0 ) throw(): ptr( p ){}

odd_ptr( odd_ptr<T> &rhs ) throw(): ptr( rhs.release() ){}

odd_ptr( odd_ptr_ref<T> rhs ) throw(): ptr( rhs.r.release() ){}

~odd_ptr() throw()

{

delete ptr;

}

odd_ptr<T> & operator=( odd_ptr<T> & rhs ) throw()

{

reset( rhs.release() );

return ( *this );

}

template <typename U>

odd_ptr<T> & operator=( odd_ptr<U> &rhs ) throw()

{

reset( rhs.release() );

return ( *this );

}

odd_ptr<T> & operator=( odd_ptr_ref<T> rhs ) throw()

{

reset( rhs.r.release() );

return ( *this );

}

template <typename U>

operator odd_ptr_ref<U>() throw()

{

return ( odd_ptr_ref<U>( *this ) );

}

T & operator*() const throw()

{

return ( *ptr );

}

T * operator->() const throw()

{

return ( return ( ptr ) );

}

T * get() const throw()

{

return ( ptr );

}

T * release() throw()

{

T *p = ptr;

ptr = 0;

return ( p );

}

void reset( T *p = 0 )

{

if ( ptr != p ) delete ptr;

ptr = p;

}

private:

T *ptr;

};



And I have defined two class for testing

class X

{

public:

virtual ~X(){}

virtual std::ostream & out( std::ostream &os ) const

{

os << "Inside X object\n";

return ( os );

}

};

class Y: public X

{

public:

virtual ~Y(){}

virtual std::ostream & out( std::ostream &os ) const

{

os << "Inside Y object\n";

return ( os );

}

};

inline std::ostream & operator<<( std::ostream &os, const X &rhs )

{

return ( rhs.out( os ) );

}


template <typename T>

inline bool operator!( const odd_ptr<T> &rhs )

{

return ( !rhs.get() );

}

template <typename T>

inline odd_ptr<T> f()

{

return ( odd_ptr<T>( new T( 0 ) ) );

}

The following code is comliled well

odd_ptr<Y> py1( new Y );


std::cout << *py1 << std::endl;


odd_ptr<X> px1;


px1 = py1;

Here template <typename U> odd_ptr<T> & operator=( odd_ptr<U> & rhs )
throw() is called.

However the code

px1 = f<Y>();

is not compiled. I though that by analogy with assignment of objects of the
same types at first template <typename U> operator odd_ptr_ref<U>() will be
called and then idd_ptr<T> & operator=( odd_ptr_ref<T> &rhs ) will be called
too.

However I get an error. It seems that the compiler does not want to call the
conversion operator to convert odd_ptr<U> to odd_ptr_ref<T>. What is the
matter?



Vladimir Grigoriev
Igor Tandetnik
2009-10-08 16:00:07 UTC
Permalink
Post by Vladimir Grigoriev
The following code is comliled well
odd_ptr<Y> py1( new Y );
std::cout << *py1 << std::endl;
odd_ptr<X> px1;
px1 = py1;
Here template <typename U> odd_ptr<T> & operator=( odd_ptr<U> & rhs )
throw() is called.
However the code
px1 = f<Y>();
is not compiled.
f<Y>() returns a temporary, which cannot bind to a non-const reference, so operator=( odd_ptr<U>& rhs) can't be used.
Post by Vladimir Grigoriev
I though that by analogy with assignment of objects
of the same types at first template <typename U> operator
odd_ptr_ref<U>() will be called and then idd_ptr<T> & operator=(
odd_ptr_ref<T> &rhs ) will be called too.
However I get an error. It seems that the compiler does not want to
call the conversion operator to convert odd_ptr<U> to odd_ptr_ref<T>.
What is the matter?
Try pulling odd_ptr_ref out of odd_ptr class and put it at namespace scope. You see, odd_ptr<T>::operator= accepts
odd_ptr<T>::odd_ptr_ref<U>. But odd_ptr<U> can only convert itself to odd_ptr<U>::odd_ptr_ref<U>. These are two unrelated classes.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going
to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Igor Tandetnik
2009-10-08 16:08:37 UTC
Permalink
Post by Igor Tandetnik
Try pulling odd_ptr_ref out of odd_ptr class and put it at namespace
scope. You see, odd_ptr<T>::operator= accepts
odd_ptr<T>::odd_ptr_ref<U>. But odd_ptr<U> can only convert itself to
odd_ptr<U>::odd_ptr_ref<U>. These are two unrelated classes.
I mean, operator= accepts odd_ptr<T>::odd_ptr_ref<T>, but the conversion can only produce odd_ptr<U>::odd_ptr_ref<T>
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going
to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Victor Bazarov
2009-10-07 12:08:32 UTC
Permalink
Post by David Wilkinson
Post by Vladimir Grigoriev
Let consider the code
template <typename T>
class A
{
A( T value = 0 ){}
A( A<T> &rhs ){} // without const attribute
template <typename U>
A( A<U> &rhs ){} // without const attribute
};
main()
{
A<int> a1;
A<int> a2 = a1; // this is O'k
A<double> a3( a1 ); // this is O'k because A( A<U> &rhs )
is called
A<double> a4 = a1; // here is a compilation array
}
What should I add (maybe some operato function) rthat the last
statement will be compiled? I.e. what is the appropriate sequence of
type conversions that is needed to compile the code taking into
account that the template constructor with const reference should not
be used?
Code compiles for me on VS2008 (if I correct to int main()).
Disable the language extensions or/and crank up the warning level.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Victor Bazarov
2009-10-07 12:07:59 UTC
Permalink
Post by Vladimir Grigoriev
Let consider the code
template <typename T>
class A
{
A( T value = 0 ){}
A( A<T> &rhs ){} // without const attribute
template <typename U>
A( A<U> &rhs ){} // without const attribute
};
main()
{
A<int> a1;
A<int> a2 = a1; // this is O'k
Yes, because both 'a1' and 'a2' have the same type.
Post by Vladimir Grigoriev
A<double> a3( a1 ); // this is O'k because A( A<U> &rhs ) is
called
Yes, without temporaries - it's called "direct initialisation".
Post by Vladimir Grigoriev
A<double> a4 = a1; // here is a compilation array
"Array"?

This would be an error because it involves a temporary.
Post by Vladimir Grigoriev
}
What should I add (maybe some operato function) rthat the last statement
will be compiled?
The problem with the last statement is that it does not represent direct
initialization. It's copy initialization that actually (semantically)
looks like this:

A<double> a4(A<double>(a1));

While the expression inside the outer set of parentheses can be
constructed using your constructors ('a1' is a "real" object), the
construction of 'a4' cannot be done using the constructors provided
since it would require to initialise the argument with the temporary (of
type A<double>). Since the argument is a reference to non-const
A<double> (in this case), it cannot be bound to a temporary.
Post by Vladimir Grigoriev
I.e. what is the appropriate sequence of type conversions
that is needed to compile the code taking into account that the template
constructor with const reference should not be used?
Drop the '=' and do direct initialization or use another object (not a
temporary).

A<double> a4(a1); // just like with 'a3'

or

A<double> a_temp(a1);
A<double> a4 = a_temp;

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Continue reading on narkive:
Loading...