template copy constructor vs normal copy constructor

C

cinsk

hi.

I just read the definition of the std::pair in <utility>. std::pair
has following two copy constructors:

template <class T1, class T2>
struct pair {

pair::pair(const pair &);

template<class U1, class U2> pair::pair(const pair<U1, U2> &);
...
};

The definition of both copy constructor looks same in my gentoo Linux/
gcc 4.4.3.

Although I'm not good at template, both copy constructors look same to
me.

Q1. What is the purpose of the second copy constructor?
Could you give me some short example code that actually calls
the second copy constructor?

Q2. What happen if the second copy construct is missing?

Q3. How can the compiler determine which copy constructor is used?
In my poor understanding, it looks like that compile would
complain for the ambiguity.

Q4. If I want to make a template class, do I need to provide both copy
constructor?
If not, which is better? And why?

Thank you.
 
K

Kai-Uwe Bux

cinsk said:
hi.

I just read the definition of the std::pair in <utility>. std::pair
has following two copy constructors:

template <class T1, class T2>
struct pair {

pair::pair(const pair &);

template<class U1, class U2> pair::pair(const pair<U1, U2> &);
...
};

The definition of both copy constructor looks same in my gentoo Linux/
gcc 4.4.3.

Although I'm not good at template, both copy constructors look same to
me.

Q1. What is the purpose of the second copy constructor?
Could you give me some short example code that actually calls
the second copy constructor?

It allows to copy construct a std::pair<double,double> from a
std::pair said:
Q2. What happen if the second copy construct is missing?

The above would not be possible
Q3. How can the compiler determine which copy constructor is used?
In my poor understanding, it looks like that compile would
complain for the ambiguity.

The ambiguity you worry about arises as copy-constructing std::pair<int,int>
from std::pair<int,int> matches both. That is true. In that case, overload
resolution kicks in. In this case, the ambiguity is resolved in favor of the
non-templated constructor.
Q4. If I want to make a template class, do I need to provide both copy
constructor?
If not, which is better? And why?

Unless you have the situation where you want a Foo<T> to be copy-
constructible from Foo<S>, you should just go with the first.


Best

Kai-Uwe Bux
 
C

cinsk

It allows to copy construct a std::pair<double,double> from a


The above would not be possible


The ambiguity you worry about arises as copy-constructing std::pair<int,int>
from std::pair<int,int> matches both. That is true. In that case, overload
resolution kicks in. In this case, the ambiguity is resolved in favor of the
non-templated constructor.


Unless you have the situation where you want a Foo<T> to be copy-
constructible from Foo<S>, you should just go with the first.

Best

Kai-Uwe Bux

Thank you for the kind answers!

Regards,
 
J

Juha Nieminen

Kai-Uwe Bux said:
It allows to copy construct a std::pair<double,double> from a
std::pair<int,int>.

Of course this could raise the question of why that *first* copy
constructor is necessary at all, then (because wouldn't the second
one cover also the case where the types are the same)?

IIRC there was some kind of special rule about constructors and
templates in play here. Could someone refresh my memory?
 
B

Bo Persson

Juha said:
Of course this could raise the question of why that *first* copy
constructor is necessary at all, then (because wouldn't the second
one cover also the case where the types are the same)?

IIRC there was some kind of special rule about constructors and
templates in play here. Could someone refresh my memory?

Yes, the first constructor is a copy constructor, the second one is a
converting constructor.

§12.8 says that a template is never a copy constructor.


Bo Persson
 
A

Armen Tsirunyan

  What is the rationale for that rule?

I am not an expert, but I'll give it a try. Maybe because the copy
constructor is ALWAYS declared, and the declaration is not a template
one?
 
J

Johannes Schaub (litb)

Juha said:
What is the rationale for that rule?

In C++03, there appears to be no way to declare a copy constructor even if
that rule were absent. This is because you need the first parameter to be
based on the current class type. But if it includes template parameters of
itself, this can never be the case:

struct A {
template<typename T>
A(... /* where to put T!? */);
};

In C++0x this is possible by just using default arguments

struct A {
template<typename T = int>
A(A const&);
};

So the actual reason might be that a template is not really a function. It's
a template that first need to be instantiated.

There is another rule that a template cannot be used to instantiate a copy
constructor. But that rule's history is quite amusing. I have seen people in
here argue about it and its desired meaning, but it appears it's very
simple: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1080 .
 
J

James Kanze

In C++03, there appears to be no way to declare a copy
constructor even if that rule were absent. This is because you
need the first parameter to be based on the current class
type. But if it includes template parameters of itself, this
can never be the case:
struct A {
template<typename T>
A(... /* where to put T!? */);
};
In C++0x this is possible by just using default arguments
struct A {
template<typename T = int>
A(A const&);
};
So the actual reason might be that a template is not really
a function. It's a template that first need to be
instantiated.
There is another rule that a template cannot be used to
instantiate a copy constructor. But that rule's history is
quite amusing. I have seen people in here argue about it and
its desired meaning, but it appears it's very
simple:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1080.

It's not that simple, because when copying an object, normal
overload resolution is done. The "copy constructor" has no
priviledged role. What is important is that a template function
cannot ever be a copy constructor, which means that it won't
prevent the compiler from generating one, e.g.:

struct A
{
template<typename T>
A(T const& other) { std::cout << "Copying" << std::endl; }
};

int
main()
{
A a;
A b(a);
return 0;
}

will output nothing (since the compiler generated copy
constructor is an equally good match as the template, and when
two functions are otherwise equally good matches, the
non-template wins). Drop the const in the signature of the
constructor template, and the template will win, since it is
a better match. (The compiler generated copy constructor will
still be generated, and be used when copying a temporary.)

Of course, if the compiler generated copy constructor doesn't do
what you want, you'll have to define one in addition to the
templated one, or you'll spend a lot of time tracking down
obscure errors.
 
J

Johannes Schaub (litb)

James said:
It's not that simple, because when copying an object, normal
overload resolution is done. The "copy constructor" has no
priviledged role. What is important is that a template function
cannot ever be a copy constructor, which means that it won't
prevent the compiler from generating one, e.g.:

That's clear anyway because only a non-template constructor can be a copy
constructor. What I meant is that people disagreed on what that statement
means to never instantiate a constructor to do the copy

- Templates are just ignored when doing overload resolution when copying a
class object of type A to another object of type A
- Templates are considered, but if an instantiation is needed the program
either needs to explicitly instantiate the constructor or explicitly
specialize it to provide a suitable specialization.
- Templates are considered, but overload resolution ignores any candidates
for which argument deduction yields a parameter list with "cv T" as type.

The issue was further confused by a footnote that explicitly allowed a
template constructor to be used to copy the object in C++03, and that note
was removed from the C++0x draft. And now they get to it again and see they
shouldn't had removed that footnote.
 
J

James Kanze

That's clear anyway because only a non-template constructor
can be a copy constructor. What I meant is that people
disagreed on what that statement means to never instantiate
a constructor to do the copy
- Templates are just ignored when doing overload resolution
when copying a class object of type A to another object of
type A
- Templates are considered, but if an instantiation is needed
the program either needs to explicitly instantiate the
constructor or explicitly specialize it to provide a suitable
specialization.
- Templates are considered, but overload resolution ignores
any candidates for which argument deduction yields a parameter
list with "cv T" as type.

There's no disagreement possible. The "copy constructor" is
special only in that it will be supplied by the compiler if the
user doesn't supply one. When copying, it has no special role.
Normal overload resolution takes place.
The issue was further confused by a footnote that explicitly
allowed a template constructor to be used to copy the object
in C++03, and that note was removed from the C++0x draft. And
now they get to it again and see they shouldn't had removed
that footnote.

Footnotes aren't nominative. There was never any ambiguity in
the nominative text (§8.5/14): "The applicable constructors are
enumerated (13.3.1.3), and the best one is chosen through
overload resolution (13.3)." I don't think there is any
ambiguity there.
 
J

Juha Nieminen

Johannes Schaub (litb) said:
In C++03, there appears to be no way to declare a copy constructor even if
that rule were absent. This is because you need the first parameter to be
based on the current class type. But if it includes template parameters of
itself, this can never be the case:

struct A {
template<typename T>
A(... /* where to put T!? */);
};

I don't really understand.

struct A
{
template<typename T>
A(const T&);
};

When a copy constructor is invoked, it would match that templated
constructor above (because T = A, and hence it becomes A(const A&))
 
J

James Kanze

[...]
When a copy constructor is invoked, it would match that templated
constructor above (because T = A, and hence it becomes A(const A&))

Just to keep things straight: there is no construct in the C++
language which invokes a "copy constructor". There are
constructs which invoke constructors, but they all do classical
overload resolution, in which a copy constructor is just another
constructor.

The only time it makes any sense to speak about a copy
constructor, as opposed to a constructor in general, is when
discussion whether the compiler will generate one automatically.
(If it does, of course, this becomes just one more constructor
to consider in overload resolution.)
 
B

Bo Persson

Juha said:
I don't really understand.

struct A
{
template<typename T>
A(const T&);
};

When a copy constructor is invoked, it would match that templated
constructor above (because T = A, and hence it becomes A(const A&))

But it doesn't, as the implicitly defined A(const A&) takes
precendence. It is a better match for the overload resolution.


Bo Persson
 
J

Juha Nieminen

James Kanze said:
The only time it makes any sense to speak about a copy
constructor, as opposed to a constructor in general, is when
discussion whether the compiler will generate one automatically.
(If it does, of course, this becomes just one more constructor
to consider in overload resolution.)

What happens in C++0x if you explicitly disable the copy constructor,
and then there's a templated constructor which would match? Does the
disabling take precedence?
 
J

James Kanze

What happens in C++0x if you explicitly disable the copy
constructor, and then there's a templated constructor
which would match? Does the disabling take precedence?

Good question. If I understand it correctly, a "deleted"
function is actually defined, so would be considered in
overload resolution. Then, if overload resolution chose it,
you would get an error. This could lead to some interesting
situations:

struct C
{
C(C&) = delete;
template<typename T>
C(T const&);
};

With such a class, you could copy rvalues and const lvalues,
but not non-const lvalues (since the deleted function would
be a better match than the template).
 
J

Johannes Schaub (litb)

Juha said:
I don't really understand.

struct A
{
template<typename T>
A(const T&);
};

No, that template is not a copy constructor because the parameter is of type
"const T&", not "const A&".
 
J

James Kanze

No, that template is not a copy constructor because the
parameter is of type "const T&", not "const A&".

No. An instantiation of that template (even for A) is not a copy
constructor because the standard says that instantiations of
templates are never copy constructors. Without this special
rule, it would be a copy constructor, because when instantiated
with A, its parameter has type A const&.
 
J

Johannes Schaub (litb)

James said:
No. An instantiation of that template (even for A) is not a copy
constructor because the standard says that instantiations of
templates are never copy constructors. Without this special
rule, it would be a copy constructor, because when instantiated
with A, its parameter has type A const&.

It says that a constructor of class X whose first parameter is of type "cv
X&" and either there are no other parameters or all other parameters have
default arguments, is a copy constructor. This does not apply to the above
template, and so it is *not* a copy constructor.

If you read what I wrote carefully, you will find I have not said anything
about an instantiated specialization of it being a copy constructor or not.
Please read more careful before disagreeing.
 
B

Blanchet Florian

Johannes Schaub (litb) said:
It says that a constructor of class X whose first parameter is of type "cv
X&" and either there are no other parameters or all other parameters have
default arguments, is a copy constructor. This does not apply to the above
template, and so it is *not* a copy constructor.

I'm agree with James. It's not a copy constructor because the draft says
"A non-template constructor for class X is a copy constructor if ..."
and not only "its first parameter is of type X&,...".

If it was just "its first parameter is of type X&,...", the result of

struct A { template<class T> A(T&){std::cout << 0} };
int main() { A a; A b(a); return 0; }

would be "0", because the template constructor A<A>(A&) is a constructor
and is first parameter is of type A&. And the result isn't "0".

Even if it isn't a "strict" copy constructor, it's called "generalized
copy constructor" by some programmers (like Meyers, cf Eff++ Item 45).
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top