Why isn't this initialization using my (template) constructor? - c++

Why the output is 0003212 ?
#include <iostream>
using namespace std;
template<typename X> class C
{
public:
C() { cout<<"0";}
template<class T> C(const C<T>& c) { cout<<"1";}
C(const C<int>& c) { cout<<"2";}
template<class T> C(const C<T*>& c) { cout<<"3";}
};
int main(int argc, char* args[])
{
C<int> c1; // 0
C<double> c2; // 0
C<int*> c3; // 0
C<int> c4(c3); // 3
C<int> c5(c1); // 2
C<int> c6(c2); // 1
C<float> c7(c1); // 2
C<double> c8(c2); // ?
std::cin.get();
return 0;
}
What is invoked in the last meaning line?
I can suppose that it's some auto-created ctor
but can't figure out which one.

There are several C++ language rules in play here.
A template cannot be a copy constructor. (Standard rule 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.
If no copy constructor is defined, the compiler generates a default one (if possible). (Standard rule 12.8p7)
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. Thus, for the class definition
struct X {
X(const X&, int);
};
a copy constructor is implicitly-declared. If the user-declared constructor is later defined as X::X(const X& x, int i =0) { /∗ ... ∗/ }
then any use of X's copy constructor is ill-formed because of the ambiguity; no diagnostic is required.
If a template and non-template are an equally good match for the arguments, the non-template wins. (Standard rule 13.3.3) The rule is a big hard-to-digest mess, I'll show just the important part:
[...] a viable function F1 is defined to be a better function than another viable function F2 if [...rules about argument matching...] or, if not that, F1 is a non-template function and F2 is a function template specialization [...]
From the code you provided, only
C<int>::C(const C<int>&)
is a user-defined copy-constructor that prints 2. All X other than int don't define a copy-constructor, so the compiler creates one.
See also
Templated copy-constructor fails with specific templated type
and
initialization ignores constructor templates

It's copy constructor generated for you by compiler, and since it's best match it's selected in last case.

In last case you call default copy constructor.

Related

Default ctor not generated when there's a template copy ctor

template<class T>
class MyClass {
public:
//MyClass() = default;
template<class X>
MyClass(MyClass<X>& other)
{
val = other.getVal();
}
T getVal()
{
return val;
}
private:
T val;
};
main()
{
MyClass<double> xd; //this one causing the problem
MyClass<double> xd1(xd); //this one is fine
MyClass<int> xi(xd);
}
This code will not compile.
But if you uncomment MyClass() = default, then everything is fine.
As far as I remember the template ctor not preventing compiler from generating the default ctor and copy ctor as well.
And indeed the compiler complaining only when trying to create object through the default ctor, but not for copy construction (which in this case is compiler generated)
The code was compiled with g++ 5.4.0 and 7.1.0. Same behavior with both
This is not a copy constructor:
template<class X>
MyClass(MyClass<X>& other) {
val = other.getVal();
}
This is a constructor taking a mutable lvalue reference to many different type than this class.
This is a copy constructor:
MyClass(const MyClass&) {
// ...
}
You can test if a function bus a special function by trying to put = default after it's signature. If the compiler complains about your misuse of defaulted functions, you function is not a special one.
When any other constructor than special constructors is added, the default constructor is not generated. That function being a template makes no different effect than a non template one. In that case, MyClass() = default is indeed required.
From cppreference:
If no user-declared constructors of any kind are provided for a class type (struct, class, or union), the compiler will always declare a default constructor as an inline public member of its class.
On the contrary, if a user provided constructor is declared, no implicitly defined default constructor is defined.
And from [class.ctor]§5:
A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.4).
Since your class has a user provided constructor, no implicitly declared/defined constructor is generated. Again, you can always force it's generation with = default.

User-provided constructor vs explicitly defaulted one

N4296::12.8/11 [class.copy] told us the following:
A defaulted copy/move constructor for a class X is defined as deleted
(8.4.3) if X has:
[...]
(11.2) — a potentially constructed subobject type M (or array thereof)
that cannot be copied/moved because overload resolution (13.3), as
applied to M’s corresponding constructor, results in an ambiguity or a
function that is deleted or inaccessible from the defaulted
constructor
[...]
So it's not clear why the program
struct X
{
};
struct Y
{
X&& x;
Y(Y const&)= default;
};
int main() { }
DEMO
works fine, but the following:
struct X
{
};
struct Y
{
X&& x;
Y(Y const&);
};
Y::Y(Y const&)= default; //error
int main() { }
DEMO
[dcl.fct.def.default]/p5, emphasis mine:
A function is user-provided if it is user-declared and not
explicitly defaulted or deleted on its first declaration. A
user-provided explicitly-defaulted function (i.e., explicitly
defaulted after its first declaration) is defined at the point where
it is explicitly defaulted; if such a function is implicitly defined
as deleted, the program is ill-formed.
This makes sense as any use of a deleted function should render a program ill-formed, but with a user-provided explicitly-defaulted function, it may be impossible to diagnose this at the call site:
// y.h
struct X
{
};
struct Y
{
X&& x;
Y(Y const&);
};
// y.cpp
#include "y.h"
Y::Y(Y const&)= default; //defined as deleted
// main.cpp
#include "y.h"
int main() {
Y y = Y();
}
When compiling main.cpp, the compiler has no idea about Y's copy constructor other than that it exists; it doesn't know that it's defaulted, and can't possibly diagnose that it is actually deleted. The only place such a error can be diagnosed is where the function is explicitly defaulted.

The case when the copy-constructor implicitly defined as deleted

The section N3797::12.8/11 [class.copy] says:
An implicitly-declared copy/move constructor is an inline public
member of its class. A defaulted copy/ move constructor for a class X
is defined as deleted (8.4.3) if X has:
[...]
— a non-static data
member of class type M (or array thereof) that cannot be copied/moved
because overload resolution (13.3), as applied to M’s corresponding
constructor, results in an ambiguity or a function that is deleted or
inaccessible from the defaulted constructor
The first case about the ambiguity of corresponding copy/move constructor is quite clear. We can write the following:
#include <iostream>
using namespace std;
struct A
{
A(){ }
A(volatile A&){ }
A(const A&, int a = 6){ }
};
struct U
{
U(){ };
A a;
};
U u;
U t = u;
int main(){ }
to reflect that. But what about or a function that is deleted or inaccessible from the defaulted constructor? What's that got with a function inaccessible from the default constructor? Could you provide an example reflecting that?
Simply put:
struct M { M(M const&) =delete; };
struct X { X(X const&) =default; M m; }; // X(X const&) is actually deleted!
Implicitly-declared functions are also considered "defaulted" ([dcl.fct.def.default] / 5); a more familiar pre-C++11 example might be something like:
struct M { protected: M(M const&); };
struct X { M m; }; // X's implicit copy constructor is deleted!
Note that if you explicitly default the function after it has been declared, the program is ill-formed if the function would be implicitly deleted ([dcl.fct.def.default] / 5):
struct M { M(M const&) =delete; };
struct X { X(X const&); M m; };
X::X(X const&) =default; // Not allowed.
a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor
The wording is perhaps slightly contrived, for conciseness most certainly. The idea, as emphasised in the above, is that the function in question is the M copy constructor being overloaded in way which renders it inaccessible. So having a member of class M whose copy constructor is made protected for instance would delete the copy constructor of X. Likewise, simply deleting the copy constructor of M would have the same result.

initialization ignores constructor templates

While pursuing some errors, I stumbled upon the following behavior of initialization, which seems odd to me: While initialization checks for existing constructors, there seem to be cases were templates for fitting constructors are ignored. Consider for example the following program:
#include <iostream>
template<class T>
struct A {
A() {};
template<class S>
A(const A<S>& a) {std::cout << "constructor template used for A" << std::endl;};
};
template<class T>
struct B{
B() {};
B(const B<int>& b) {std::cout << "constructor used for B" << std::endl;};
};
int main() {
A<int> a;
B<int> b;
A<int> aa = a;
B<int> bb = b;
A<double> aaa = a;
}
For me, this produces the output
constructor used for B
constructor template used for A
this means it does not use the constructor in the third line of main. Why not? Is there a reason? Or is my syntax off somewhere? The template seems to work, as it is successfully used in the last line.
I know the example seems overly complicated, but various simplifications made the behavior I wanted to display go away. Also: A template specialization will be used by the initialization and is how I currently prevent this causing errors (where it caused errors in the first place).
I am sorry if my question is off in any way, I am not a programmer, I am not a native speaker and this is my first question, please forgive me.
The compiler provides an implicitly declared non-template copy constructor with signature equivalent to
A(const A& a);
because a template constructor is not considered as a user defined copy constructor, i.e. a copy constructor has to be a non-template.
The implicitly declared copy constructor is a better match in the overload resolution than the template version, and is the one that gets called when you copy construct an A<T> from an A<T>. This can be illustrated with a simple example, with a user defined A(const A&):
#include <iostream>
template<class T>
struct A
{
A() {};
A(const A& a) {
std::cout << "copy constructor used for A" << std::endl;
}
template<class S>
A(const A<S>& a) {
std::cout << "constructor template used for A" << std::endl;
}
};
int main()
{
A<int> ai;
A<double> ad = ai; / calls template conversion contructor
A<int> ai2 = ai; // calls copy constructor A(const A&);
}
Per Paragraph 12.8/7 of the C++11 Standard:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly.
Moreover, per Paragraph 12.8/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).
Therefore, the compiler will generate an implicit copy constructor here that gets invoked during the execution of this line:
A<int> aa = a;
This explains why you do not see a corresponding output.
This is because templated ctor is not a copy ctor. See also https://stackoverflow.com/a/1249848/341065.

template copy constructor

Given the following code, does Foo have copy constructor? Is it safe to use Foo with STL containers?
class Foo
{
public:
Foo() {}
template <typename T>
Foo(const T&) {}
};
The standard explicitly says that a copy constructor is a non-templated constructor that takes a reference to a possibly const-volatile object of the same type. In the code above you have a conversion but not copy constructor (i.e. it will be used for everything but copies, where the implicitly declared constructor will be used).
Does Foo have a copy constructor?
Yes, the implicitly declared/defined copy constructor.
Is it safe to use Foo with standard library containers?
With the current definition of Foo it is, but in the general case, it depends on what members Foo has and whether the implicitly defined copy constructor manages those correctly.
According to the Standard, a copy-constructor must be one of the following signature:
Foo(Foo &);
Foo(Foo const &);
Foo(Foo volatile &);
Foo(Foo const volatile &);
Foo(Foo&, int = 0, );
Foo(Foo&, int = 0, float = 1.0); //i.e the rest (after first) of the
//parameter(s) must have default values!
Since the template constructor in your code doesn't match with the form of any of the above, that is not copy-constructor.
Foo has a compiler generated copy constructor, which cannot be replaced by the template conversion constructor you have provided.
Foo f0;
Foo f1(f0); // calls compiler-synthesized copy constructor
Foo f2(42); // calls template conversion constructor with T=int