template copy constructor - c++

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

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.

Is it possible to have a default copy constructor and a templated conversion constructor?

If I have a class like this
template <typename T>
class MyClass
{
T myData;
public:
T getValue() { return myData; }
template <typename V>
MyClass(const MyClass<V>& other) :
myData((T) other.getValue())
{
}
};
This would mean that I provide a copy constructor (for V=T) and thus according to this link Why no default move-assignment/move-constructor? I do not get default move constructors etc.
Is there a way to have the templated constructor only work as conversion constructors, so for V!=T?
Your premise is wrong. A constructor template is never used to instantiate a copy (or move) constructor. In other words, a copy/move constructor is always a non-template member function, even if a member function template could produce a constructor with the appropriate copy/move signature.
So as is, your class will still have a normal copy constructor in addition to the template.

Why must thrown objects be copy-initialized?

Exceptions use the statical type of an object to copy-initialize the thrown object. For instance:
struct foo
{
foo() = default;
foo(const foo&) = delete;
};
int main()
{
throw foo();
}
Clang++ --std=c++14 complains that the explicitly-deleted copy constructor can't be used. Why can't it be move-initialized instead?
It can't be move constructed because the type has no move constructor. A deleted copy constructor suppresses the implicit move constructor.
Modify the code to the following:
struct foo
{
foo() = default;
foo(const foo&) = delete;
foo(foo&&) = default;
};
int main()
{
throw foo();
}
Read this, the section "Implicitly-declared move constructor".
Because foo(foo&& ); is missing. By deleteing the copy constructor you've supressed move constructor as well.
The applicable phrasing from the standard (§[class.copy]/9) looks roughly like this (well, exactly like this, as of N4296):
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if:
X does not have a user-declared copy ctor,
[...]
This applies because defining the copy ctor as deleted still means you've declared the copy ctor.

Why does `std::pair<int, movable>` require a [deleted] `const&` copy constructor?

I'm busy testing an implementation of various generic algorithms and I'm using types with minimal support of provided functions. I came across this weird setup when using a std::pair<T, movable> with some type T (e.g., int) and a movable type defined like this:
struct movable
{
movable() {}
movable(movable&&) = default;
// movable(movable const&) = delete;
movable(movable&) = delete;
};
The idea is have a type which is movable but not copyable. That works great, e.g., with expressions like this:
movable m1 = movable();
movable m2 = std::move(m1);
However, when trying to use this type as a member of std::pair<...> it fails! To make get the code to compile it is necessary to add the deleted(!) copy constructor taking a movable const& (or have only that version). The copy constructor taking a non-const reference is insufficient:
#include <utility>
auto f() -> std::pair<int, movable> {
return std::pair<int, movable>(int(), movable());
}
What is going on here? Is std::pair<...> overspecified by mandating that std::pair(std::pair const&) is = defaulted?
The problem seems to be down to the specification of std::pair's copy constructor (in 20.3.2 [pairs.pair] synopsis):
namespace std {
template <class T1, class T2>
struct pair {
...
pair(const pair&) = default;
...
};
}
A quick check with my implementation implies that the obvious implementation copying the two members does not require the const& version of the movable copy constructor. That is, the offensive part is the = default on pair's copy constructor!
std::pair copy constructor is declared as follows:
pair(const pair&) = default;
By declaring this copy constructor for movable:
movable(movable&) = delete;
you inhibit implicit creation of movable(const movable&) (so it's not even deleted, there's just no such constructor), thus this is the only copy constructor you have. But std::pair copy constructor requires a copy constructor of its members to take const reference, so you get compile error.
If you add this:
movable(movable const&) = delete;
or (better) just remove movable(movable&) = delete; declaration, you now have the movable(movable const&) constructor, and because it's deleted, the std::pair copy constructor also becomes deleted.
Update: Let's consider a simpler example demonstrating the same issue. This doesn't compile:
template <typename T>
struct holder {
T t;
// will compile if you comment the next line
holder(holder const&) = default;
// adding or removing move constructor changes nothing WRT compile errors
// holder(holder&&) = default;
};
struct movable {
movable() {}
movable(movable&&) = default;
// will also compile if you uncomment the next line
//movable(movable const&) = delete;
movable(movable&) = delete;
};
holder<movable> h{movable()};
It will compile if you comment the copy constructor of holder, because this is how implicit copy constructor generation works ([class.copy]/8:
The implicitly-declared copy constructor for a class X will have the form
X::X(const X&)
if each potentially constructed subobject of a class type M (or array thereof) has a copy constructor whose first parameter is of type const M& or const volatile M&. Otherwise, the implicitly-declared copy constructor will have the form
X::X(X&)
That is, when you comment out the declaration holder(holder const&) = default; the implicitly declared copy constructor of holder will have the form holder(holder&). But if you don't, T's copy constructor has take const T& (or const volatile T&) because this is what will be called in memberwise copy procedure described in [class.copy]/15.
And if holder has a move constructor, it's even easier - if you comment out holder(holder const&) = default;, the implicitly declared copy constructor of holder will be just deleted.

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

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.