How to define a single copy constructor for template classes? - c++

#include <iostream>
template <typename T>
class Matrix
{
public:
Matrix() = default;
template <typename U>
Matrix(const Matrix<U>& matrix) {
std::cout << "Copying internal data..." << std::endl;
}
// Matrix(const Matrix<T>& matrix) {
// std::cout << "Copying internal data..." << std::endl;
// }
Matrix(Matrix<T>&& matrix) {
std::cout << "Moving internal data..." << std::endl;
}
};
int main() {
Matrix<int> m1{};
Matrix<double> m2 = m1;
Matrix<int> m3 = m1;
}
Here, I have a matrix class, it can be a matrix of int, a double, or any numerical value.
I want to define a copy constructor that accepts a matrix with any numerical type and copies its elements.
For example, suppose m1 is a Matrix<double> = {1.1, 2.2, 3.3, ...}, Matrix<int> m2 = m1 should set m2 to be {1, 2, 3, ...}.
Also, I want to have a move constructor, but it doesn't make any sense to have a move constructor for any type except for its own type (in this example, it's T).
This is because I'm going to steal the pointer pointing to the array of numbers, and to do so, it has to be of the same type.
Defining a move constructor that accepts only Matrix<T> automatically deletes the copy constructor for Matrix<T>.
I realized that since the parameter in the copy constructor I tried to make isn't necessarily of the same type, it's not considered to be a copy constructor, and unless I write a copy constructor specifically for Matrix<T> (the commented copy constructor), the code won't compile.
But even if I don't have a copy constructor, I have a constructor that accepts a matrix of any type. Why is it looking specifically for the copy constructor?
How do I define my copy constructor only once, and have it deal with matrices of any type?

But even if I don't have a copy constructor, I have a constructor that accepts a matrix of any type. Why is it looking specifically for the copy constructor?
In overload resolution it is not find the best usable function. Instead, it is find the best function and try to use it. If it can't because of access restrictions or being deleted, then you get a compiler error.
In your case, you have the template constructor that stamps out Matrix(const Matrix<double>& matrix) (#1), and it also finds Matrix(const Matrix<double>& matrix) = delete (#2) which was implicitly generated by the compiler because you have a user provided move constructor. In overload resolution, if two functions have the exact same signature, and one of those is a specialization of a template, the non template version is chosen. In this case that is #2 which is deleted, so you get an error for accessing a deleted function.
So to answer
How do I define my copy constructor only once, and have it deal with matrices of any type?
You can't. If you want Foo<T> to be copyable from another Foo<T> then you need to provide a copy constructor. If you want Foo<T> to be copyable from a Foo<U>, then you need to add a converting constructor for that. Ideally you would use something like a std::vector as the underlying storage for the matrix elements, and if you do, then you can just follow the rule of zero and your class becomes
template <typename T>
class Matrix
{
public:
Matrix() = default;
template <typename U>
Matrix(const Matrix<U>& other) data(other.data.begin(), other.data.end()) {}
private:
std::vector<T> data;
};

Related

C++ vector emplace_back calls copy constructor

This is a demo class. I do not want my class to be copied, so I delete the copy constructor. I want vector.emplace_back to use this constructor 'MyClass(Type type)'. But these codes won't compile. Why?
class MyClass
{
public:
typedef enum
{
e1,
e2
} Type;
private:
Type _type;
MyClass(const MyClass& other) = delete; // no copy
public:
MyClass(): _type(e1) {};
MyClass(Type type): _type(type) { /* the constructor I wanted. */ };
};
std::vector<MyClass> list;
list.emplace_back(MyClass::e1);
list.emplace_back(MyClass::e2);
The copy constructor is required by vector so that it can copy the element when it need to grow its storage.
You can read the document for vector
T must meet the requirements of CopyAssignable and
CopyConstructible. (until C++11)
The requirements that are imposed on
the elements depend on the actual operations performed on the
container. Generally, it is required that element type is a complete
type and meets the requirements of Erasable, but many member functions
impose stricter requirements. (since C++11) (until C++17)
The
requirements that are imposed on the elements depend on the actual
operations performed on the container. Generally, it is required that
element type meets the requirements of Erasable, but many member
functions impose stricter requirements. This container (but not its
members) can be instantiated with an incomplete element type if the
allocator satisfies the allocator completeness requirements.
Some logging can help you understand what's going on
For this code
class MyClass
{
public:
typedef enum
{
e1 = 1,
e2 = 2,
e3 = 3,
} Type;
private:
Type _type;
public:
MyClass(Type type): _type(type) { std::cout << "create " << type << "\n"; };
MyClass(const MyClass& other) { std::cout << "copy " << other._type << "\n"; }
};
int main() {
std::vector<MyClass> list;
list.reserve(2);
list.emplace_back(MyClass::e1);
list.emplace_back(MyClass::e2);
list.emplace_back(MyClass::e3);
}
The output is
create 1
create 2
create 3
copy 1
copy 2
So you can emplace_back does use the desired constructor to create the element and call copy constructor when it need to grow the storage. You can call reserve with enough capacity upfront to avoid the need to call copy constructor.
If for some reason you really don't want it to be copy constructible, you can use std::list instead of std::vector as list is implemented as linked list, it doesn't need to move the elements.
http://coliru.stacked-crooked.com/a/16f93cfc6b2fc73c
Just a precision for the issue. If we don't want objects copy construction to be used when a reallocation of the container occurs, it is indeed possible with a move constructor but only if it has the noexcept specification.
Containers refuse to move construct elements if the constructor might throw an exception because it could lead to a container in a bad state that cannot be cleaned. That's the reason why it is generally a good practice to specify a move constructor as noexcept when we are sure it will never throw any exceptions.
According to https://en.cppreference.com/w/cpp/container/vector/emplace_back, the value_type of a std::vector<T> needs to be MoveInsertable and EmplaceConstructible. MoveInsertable in particular requires a move constructor or a copy constructor.
So, if you don't want your class to be copied, you should add an explicit move constructor. You can use = default to use the compiler-provided default implementation that just moves all fields.
Full example
#include <vector>
class MyClass
{
public:
typedef enum
{
e1,
e2
} Type;
private:
Type _type;
MyClass(const MyClass& other) = delete; // no copy
public:
MyClass(): _type(e1) {};
MyClass(MyClass&&) noexcept = default; // < the new move constructor
MyClass(Type type): _type(type) { /* the constructor I wanted. */ };
};
int main() {
std::vector<MyClass> list;
list.emplace_back(MyClass::e1);
list.emplace_back(MyClass::e2);
}
Note
Note that you can get a very confusing
error: use of deleted function ‘MyClass::MyClass(const MyClass&)
with C++17 when you use
auto x = list.emplace_back(MyClass::e1);
instead of
auto& x = list.emplace_back(MyClass::e1);
even with the move constructor.

Define Only Non-Trivial Copy Operations

I have a templated class which has many member variables. A small number of these variables have the template type of the class, the majority have fixed types.
I'd like to copy between one instance of the class to another with conversions, but cannot do so using implicit copy if the classes do not have the same type. Therefore, I need an assignment method.
However, it feels unfortunate to have to write out all of those many copy operations just to do the conversion I want.
Therefore, is there a way to set up the assignment operator such that implicit copies are done where possible?
An example code follows:
#include <iostream>
template<class T>
class MyClass {
public:
int a,b,c,d,f; //Many, many variables
T uhoh; //A single, templated variable
template<class U>
MyClass<T>& operator=(const MyClass<U>& o){
a = o.a; //Many, many copy operations which
b = o.b; //could otherwise be done implicitly
c = o.c;
d = o.d;
f = o.f;
uhoh = (T)o.uhoh; //A single converting copy
return *this;
}
};
int main(){
MyClass<int> a,b;
MyClass<float> c;
a.uhoh = 3;
b = a; //This could be done implicitly
std::cout<<b.uhoh<<std::endl;
c = a; //This cannot be done implicitly
std::cout<<c.uhoh<<std::endl;
}
There are 2 naïve ways:
Create a function CopyFrom(const MyClass& o) that copy the copiable values
then you call it from the operator= overload plus eventually template specialization depending on your needs.
Pack all the copiable values in a subclass/struct, you'll be able to use the default operator= generated by your compiler ;)

c++ custom container pass {} list

I am basically remaking the set container and I am wondering how I pass a brace-enclosed initializer list to the container.
mySet<int> myset = {"test", "test 2"};
I tried to overload the operator= to take a list as a parameter.
Despite the presence of the = here, this is constructing a set, so what you need is a constructor (that takes an std::initializer_list as its parameter).
template<class T>
class myset {
public:
// ...
mySet(std::initializer_list<T> init) {
for (T const &t : init)
insert(t);
}
};
You need to implement a constructor which take a std::initializer_list as its parameter, such as:
template <class T>
class mySet {
public:
mySet(std::initializer_list<T> l) {
// ...
}
};
Define a constructor that takes a std::initializer_list as a parameter, like
mySet(std::initializer_list<T> args){/*...*/}
operator= is invoked only on assignments, not on copy initialization (your case). In this latter case the constructor is invoked on the rhs, followed by a copy (or move), which is usually elided, but nevertheless the copy or move ctors must be accessible.

Questions about a piece of code with templates, conversion operator and copy ctor

Two questions about the following piece of code:
template <class T> class A {
protected:
T j;
public:
A(T k) :j(k) {cout << *this;}
~A() { cout << *this; }
A(const A<T> &a) {
j = a.j;
cout << *this;
}
virtual void print() const {cout << j << ' ';}
friend ostream &operator << (ostream &os, const A<T> &a) {
a.print();
return os;
}
operator T() { return j;}
};
template <class T> class inherit:public A<T> {
T field;
public:
inherit(const T&t) :A<T>(t), field(1+t) {
cout << *this;
}
void print() const {
A<T>::print();
cout << field << ' ';
}
};
int main(){
inherit <int> b(3);
inherit <string> c("asdf");
string k="str";
c + k;//error no operator +
b + 5;//no error
}
Why does inherit <int> b(3); leads to the copy ctor of inherit? Why copy instead of making a new instance of inherit from scratch using the default ctor?
Why does b+5; leads to the conversion operator operator T() and why it doesn't happen with c+k?
Why does inherit <int> b(3); leads to the copy ctor of inherit? Why copy instead of making a new instance of inherit from scratch using the default ctor?
Firstly, it does not lead to the copy constructor and the instance is in fact made from scratch.
The default constructor was not used because you didn't call the default constructor. The default constructor would be called with empty argument list (except, in this case, you must also leave out the parenthesis to avoid the vexing parse):
inherit <int> b; // this would call the default constructor
If you pass an argument to the constructor, then a non-default constructor will be invoked. inherit <int> b(3); leads to a call to inherit(const T&) which in this template instance is inherit(const int&). It is not the copy constructor of inherit.
Why does b+5; leads to the casting operator operator T()
Because there is no operator+(const inherit<int>&, int) nor the analogous member function defined. Therefore, the overload resolution looks for alternatives to which the operands can be implicitly converted. It just so happens, that a built-in operator+(int, int) exists, and inherit<int> can implicitly be converted to A<int> (because it's a base) and A<int> can be converted to an int (because of the casting operator). And so, that operator ends up being called.
and why it doesn't happen with c+k?
Firstly, you cannot even instantiate inherit <string> because the constructor tries to add an int to the argument string, which has no valid overload.
Now, assuming that constructor was fixed so that inherit<string> can exist, c + k still doesn't seem to work. I suspect that's because the string needs more conversions than int because it's not a primitive and you've reached the maximum depth that a user-defined conversion sequence can have. You can explicitly cast inherit<string> to string to shorten the conversion sequence:
static_cast<std::string>(c) + k; // this works
Why does b+5; leads to the conversion operator operator T() and why it doesn't happen with c+k?
The compiler is complaining about a completely different piece of code. If you remove the + inside main(), you can see it still complains about an operator+:
http://melpon.org/wandbox/permlink/H3cUUaf8fSnbYDwA
The reason for this is on this line:
inherit(const T&t) :A<T>(t), field(1+t) {
you have 1 + t, where t is std::string. std::string has no operator+ for int, so this does not compile.

Finding typeid of a template parameter

The print statement in the constructor's definition doesn't get printed, isn't the constructor calling correct in main? I know I am missing some point here, please point out.
#include <iostream>
#include <typeinfo>
template <typename T> class List
{
public:
template <typename T2> List (List<T2> const&);
};
template <typename T> template <typename T2> List <T> :: List (List <T2> const&)
{
std :: cout << "\nType name:" << typeid (T2).name();
}
int main ()
{
List <int> kk (List <int>);
return 0;
}
There are a couple of things wrong in your code that you might not be aware of.
List<int> kk( List<int> );
That line is not a variable definition, but rather the declaration of a function that takes a List<int> as argument and returns a List<int>, so that effectively will not call any constructor. That is know as the most-vexing-parse (you can look at different versions of it by searching in SO, or in the C++ FAQ lite)
The second issue is that you cannot possibly create any instance of the an instantiated type of List, the reason being is that the only constructor that you are providing is a templated constructor that takes a second List<U> as argument. That effectively disables the default constructor, so the only way of creating a List<T> is by already having a List<U>, and that is not possible. You can add the default constructor back:
template <typename T>
class List {
public:
List() {}
template <typename U>
List( List<U> const & ) {} // prefer const& as that will avoid unnecessary copying
};
And now you can write:
List<int> l = List<int>(); // this will call List<int>::List( List<int> const & )
And yet, that will still not call the constructor you want. The reason is a little obscure, but when copy constructing an element of a template, the compiler will not use a templated constructor. In the code above, it will implicitly define a copy constructor by doing member-wise copy constructor of the methods and call that generated constructor. That means that in most occasions where you want to provide a templated constructor you want to also provide a non-templated copy constructor.
To actually call that constructor you would have to provide a different type:
List<int> l = List<double>();
Since the types actually differ, the compiler cannot copy construct, will find that the provided templated constructor is the best overload candidate and call it.
As well as the "most vexing parse" identified by David:
you need to have at least one more constructor to create the original List object to be passed to the copy constructor,
you need to vary the parameter type in order to have the templated copy constructor invoked: as is you'll match the implicitly declared List(const List&) copy constructor instead.
So:
#include <iostream>
template <typename T>
struct X
{
X() { std::cout << "X()\n"; }
// implicitly like this anyway...
// X(const X& rhs) { std::cout << "X(X&)\n"; }
template <typename U>
X(const U& u) { std::cout << "U\n"; }
};
int main()
{
X<int> x;
X<int> y(x);
}
What are you trying to do with this statement:
List <int> kk (List <int>);
(It actually declares a function, and can't be anything but
a function declaration.)
In order to see output from the copy constructor, you've got to
invoke the copy constructor somehow. Which means having an
object to copy. Which isn't possible with the code you've
given: since you've explicitly declared a constructor, the
compiler will not provide a default constructor, and you have no
other constructor with which to create an object. So you have
no way of creating anything to copy. If you add a
List() {}
to the class, and write:
List<int> kk((List<int>());
, you might get something, but the compiler is allowed to elide
the copy here, so more likely there will be no output. Try:
List<int> a;
List<int> b(a);
Or just put your output in the default constructor.