Copy constructor of template class - c++

I read that template copy-con is never default copy onstructor, and template assignment-op is never a copy assignment operator.
I couldn't understand why this restriction is needed and straight away went online to ideone and return a test program but here copy constructor never gets called on further googling I came across templatized constructor and tried that but still it never calls copy constructor.
#include <iostream>
using namespace std;
template <typename T> class tt
{
public :
tt()
{
std::cout << std::endl << " CONSTRUCTOR" << std::endl;
}
template <typename U> const tt<T>& operator=(const tt<U>& that){std::cout << std::endl << " OPERATOR" << std::endl;}
template <typename U> tt(const tt<U>& that)
{
std::cout << std::endl << " COPY CONSTRUCTOR" << std::endl;
}
};
tt<int> test(void)
{
std::cout << std::endl << " INSIDE " << std::endl; tt<int> a; return a;
}
int main() {
// your code goes here
tt<int> a ; a = test();
return 0;
}
Can someone explain me the whole reason behind putting this restriction and also how to write a copy constructor of template class.
Thanks

I can't comment on why this is how it is, but here's how you write a copy constructor and assignment operator for a class template:
template <class T>
class A
{
public:
A(const A &){}
A & operator=(const A& a){return *this;}
};
and that's it.
The trick here is that even though A is a template, when you refer to it inside the class as A (such as in the function signatures) it is treated as the full type A<T>.

There are strict rules what constitutes a copy constructor (cf. C++11, 12.8):
It is not a template.
For a class T, its first argument must have type T & or T const & or T volatile & or T const volatile &.
If it has more than one argument, the further arguments must have default values.
If you do not declare a copy constructor, a copy constructor of the form T::T(T const &) is implicitly declared for you. (It may or may not actually be defined, and if it is defined it may be defined as deleted.)
(The usual overload resolution rules imply that you can have at most four copy constructors, one for each CV-qualification.)
There are analogous rules for move constructors, with && in place of &.

Related

Why can't a copy constructor be templated? [duplicate]

This question already has answers here:
Copy constructor of template class
(2 answers)
What is a converting constructor in C++ ? What is it for?
(3 answers)
Closed 1 year ago.
I am doing a course C++ Fundamentals for Professionals on educative.io website, it mentioned the above statement added in the question,
I created a small c++ program to experiment the same -
#include<iostream>
using namespace std;
template<typename T1>
class MyTemplateClass
{
public:
T1 data;
MyTemplateClass()
{
cout << "in default const" << endl;
}
MyTemplateClass(const MyTemplateClass& other)
{
cout << "in default copy const - created automatically" << endl;
this->data = other.data;
}
template<typename T>
MyTemplateClass(const MyTemplateClass<T>& other)
{
cout << "in templated copy const" << endl;
this->data = other.data;
}
};
int main()
{
cout << "hello world" << endl;
MyTemplateClass<int> obj;
MyTemplateClass<double> obj2(obj);
MyTemplateClass<int> obj3(obj);
}
For obj3 it calls the default copy constructor which makes sense but for obj2 it calls the templated copy constructor, so I see a use for the constructor that if I have a class that takes T1 = double, we can created an object for it from an object of a class that takes T1 = int.
Does obj2 using template MyTemplateClass(const MyTemplateClass& other) - this function doesn't seem like a valid copy constructor according to the rules of a copy constructor defined in c++ but why is it such a case - since this seems like a valid use case?
A constructor template doesn't count as copy constructor even if it has the correct signature of a copy constructor for some template argument.
This is important because if there is no copy constructor declared by the user, then the compiler will declare an implicit one.
So for example
struct A {
A() {}
template<typename T>
A(const T&) { std::cout << "Template called!"; }
};
int main() {
A a;
A b = a;
}
will not output Template called!. Instead of the template specialization the implicitly declared copy constructor (which does nothing in this case) is called.

Is there any way to achieve move elision

When running the following code:
struct Copy
{
Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
Copy(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
~Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
};
char buffer[1024];
template <typename Type>
Type * push(Type value)
{
return new(buffer) Type(std::move(value));
};
int main()
{
push(Copy());
return 0;
}
I get the following output:
Copy::Copy()
Copy::Copy(Copy &&)
Copy::~Copy()
Is there anyway to elide the move constructor?
I was hoping that with -O3, it would be constructed in place, but by my testing, that doesn't seem to be the case.
Is there anyway to elide the move constructor? [...] I was hoping that with -O3, it would be constructed in place,
Well, you're explicitly calling the move constructor... and the object is being constructed in-place in buffer. Why do you expect the move constructor call to be elided?
template <typename Type>
Type * push(Type value)
{
// invokes `Type::Type(Type&&)`
// vvvvvvvvvvvvvvvvvvvvvv
return new(buffer) Type(std::move(value));
// ^^^^^^^^^^^^^^^^
// casts `value` to `Type&&`
};
Your question would make more sense if you were trying to construct Copy with a value of some other type. E.g.:
struct Copy
{
Copy(int) { std::cout << "INT\n"; }
// ... other ctors ...
};
template <typename Type, typename Arg>
Type * push(Arg x)
{
return new(buffer) Type(std::move(x));
};
int main()
{
push<Copy>(1);
}
The code above prints:
INT
No move/copy constructor is invoked.
live example on wandbox
I do not think you can do this. Because elision requires the compiler to have an intrinsic knowledge of where the objects are being constructed. And with that information, it can just avoid moves and copies and simply place the object where it needs to go. For example when you return something from the stack of one function back to another, the compiler can elide the move/copy.
But in your case placement new allows you to construct an object into any arbitrary buffer. And that buffer can really be anywhere, for example it can be on the stack (like in your case) or it can be allocated on the heap with new. So the compiler does not elide the move/copy here.
Strictly speaking, this can happen through some analysis of the code since the compiler already knows where the buffer is, but I doubt most compilers implement this.
Note note that you have declared an array of character pointers and not characters, so the buffer is more than 1024 bytes in length if that is what you were expecting
Note Also note that calling std::move explicitly can prevent elision
The best you can do in this case is make an in place constructor or a move constructor (as you have above) to construct that object into the buffer. An in place constructor would look something like this
template <typename... args>
void construct(std::in_place_t, Args&&... args) {
new (buffer) Type{std::forward<Args>(args)...};
}
Use an emplace function with perfect argument forwarding. There are a few more things to say since you're about to embark on an adventure in placement new:
Use std::aligned_storage_t<> for the storage. It guarantees that your objects will be properly aligned.
Do read and use the return value of placement new. In simple cases it won't be different from the address you provide. However the standard allows it to be, and in complex class hierarchies it might be.
updated example:
#include <iostream>
struct Copy
{
Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
Copy(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
~Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
};
std::aligned_storage_t<sizeof(Copy), alignof(Copy)> buffer[4];
template <typename Type, typename LegalStorage, typename...Args>
auto emplace(LegalStorage* buffer, Args&&...args) -> Type*
{
return new(buffer) Type(std::forward<Args>(args)...);
};
int main()
{
auto p1 = emplace<Copy>(buffer /* constructor arguments go here*/);
auto p2 = emplace<Copy>(&buffer[1]/* constructor arguments go here*/);
auto p3 = emplace<Copy>(buffer + 2/* constructor arguments go here*/);
auto p4 = emplace<Copy>(buffer + 3/* constructor arguments go here*/);
return 0;
}

Why compilers generate a copy/move constructors when there is a templated constructor?

Mechanism for this is well explained here: Template "copy constructor" does not prevent compiler-generated move constructor, but I would like to better undestand why it is made this way. I understand that move constructor is not generated even if any other constructor is written by the programmer, because it is an indication that construction of object is non trivial and auto generated constructor will probably be wrong. Then why templated constructors that have the same signature as copy constructors are not simply named copy constructors?
Example:
class Person {
public:
template<typename T>
Person(T&& t) : s(std::forward<T>(t)) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
Person(int n) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
// No need to declare copy/move constructors as compiler will do this implicitly
// Templated constructor does not inhibit it.
//Person(const Person&) = default;
//Person(Person&&) = default;
private:
std::string s;
};
and then:
Person p("asd"); // OK!
//Person p4(p); // error as Person(T&&) is a better match
if I make p const:
const Person p("asd");
Person p4(p); // thats ok, generator constructor is a better match
but if I explicitly delete even a move constructor with:
Person(Person&&) = delete;
then auto generation of constructors is inhibited.
You understand wrong.
struct noisy {
noisy() { std::cout << "ctor()\n"; }
noisy(noisy&&) { std::cout << "ctor(&&)\n"; }
noisy(noisy const&) { std::cout << "ctor(const&)\n"; }
noisy& operator=(noisy&&) { std::cout << "asgn(&&)\n"; return *this; }
noisy& operator=(noisy const&) { std::cout << "asgn(const&)\n"; return *this; }
};
struct test {
noisy n;
test(int x) { (void)x; }
};
test has generated move/copy construct/assignment.
Live example.
A copy/move construct/assignment written by a programmer results in the other ones being suppressed.
Now, writing a constructor suppresses the zero-argument constructor. That may be why you are confused.
Templated constructors with the same signature as copy constructors are not copy constructors because the standard says so.
As it happens, templated code is rarely the correct code for a copy or move constructor/assignment.
The fact that forwarding references often grab self& and self const&& copy/move over the actual copy/move operations is a problem. C++ isn't perfect.
Usually the way to avoid this is:
template<class T,
class=std::enable_if_t<
!std::is_same<std::decay_t<T>, Person>::value
>
>
Person(T&& t) : s(std::forward<T>(t)) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
or !std::is_base_of<Person, std::decay_t<T>>::value which covers some other situations (like inheriting constructors).

Why instantiation of template of stream operator occurs instead of global overloaded operator?

Assuming following code. There is class MyStream witch has template overloaded operator <<. There also is globally overloaded operator MyStream& operator << (MyStream&, const MyClass&). The confusing thing is generating (by compiler) different methods for two almost identical situations (see body of main() function). I supposed that global operator should be used in both cases but it isn't. Why so?
#include <iostream>
class MyStream;
class MyClass;
MyStream& operator << (MyStream& stream, const MyClass&);
class MyStream
{
public:
template <typename T>
MyStream& operator << (const T&)
{
std::cout << __FUNCTION__ << " " << typeid(T).name() << std::endl;
return *this;
}
};
class MyClass
{
};
MyStream& operator << (MyStream& stream, const MyClass&)
{
std::cout << __FUNCTION__ << " " << typeid(MyClass).name() << std::endl;
return stream;
}
int main(int, char**)
{
// 1. Used globally defined operator for MyClass
MyStream() << int() << MyClass();
std::cout << std::endl;
// 2. Template instantiation
MyStream() << MyClass();
std::cin.get();
return 0;
}
Output of program compiled with Microsift Visual C++ Compilers 9.0 (x86):
MyStream::operator << int
operator << class MyClass
MyStream::operator << class MyClass
// 2. Template instantiation
MyStream() << MyClass();
In this case, the expression MyStream() creates a temporary object (a rvalue) which cannot be bound to non-const reference, so the compiler chooses the member function template, because in order to call the free function, the temporary object must be passed as first argument to the function, which is not possible here, as the type of first parameter of the free function is non-const reference. So MyStream << MyClass() invokes member function.
But when you write this:
// 1. Used globally defined operator for MyClass
MyStream() << int() << MyClass();
It first invokes the member function passing int(), and the member function returns an object of type MyStream& which now can be passed to free function as first argument (as it is no more a rvalue, it is now a lvalue), then it invokes the free function, passing object of type MyStream& as first argument and MyClass() as second argument.
This is interesting, and a similar thing happens here:
std::ostringstream printing the address of the c-string instead of its content

How to force the compiler to use explicit copy constructor?

I wrote a small test program with a sample class containing also self-defined constructor, destructor, copy constructor and assignment operator. I was surprised when I realized that the copy constructor was not called at all, even though I implemented functions with return values of my class and lines like Object o1; Object o2(o1);
innerclass.hpp:
#include <iostream>
class OuterClass
{
public:
OuterClass()
{
std::cout << "OuterClass Constructor" << std::endl;
}
~OuterClass()
{
std::cout << "OuterClass Destructor" << std::endl;
}
OuterClass(const OuterClass & rhs)
{
std::cout << "OuterClass Copy" << std::endl;
}
OuterClass & operator=(const OuterClass & rhs)
{
std::cout << "OuterClass Assignment" << std::endl;
}
class InnerClass
{
public:
InnerClass() : m_int(0)
{
std::cout << "InnerClass Constructor" << std::endl;
}
InnerClass(const InnerClass & rhs) : m_int(rhs.m_int)
{
std::cout << "InnerClass Copy" << std::endl;
}
InnerClass & operator=(const InnerClass & rhs)
{
std::cout << "InnerClass Assignment" << std::endl;
m_int = rhs.m_int;
return *this;
}
~InnerClass()
{
std::cout << "InnerClass Destructor" << std::endl;
}
void sayHello()
{
std::cout << "Hello!" << std::endl;
}
private:
int m_int;
};
InnerClass innerClass()
{
InnerClass ic;
std::cout << "innerClass() method" << std::endl;
return ic;
}
};
innerclass.cpp:
#include "innerclass.hpp"
int main(void)
{
std::cout << std::endl << "1st try:" << std::endl;
OuterClass oc;
OuterClass oc2(oc);
oc.innerClass().sayHello();
std::cout << std::endl << "2nd try:" << std::endl;
OuterClass::InnerClass ic(oc.innerClass());
ic = oc.innerClass();
}
Output:
1st try:
OuterClass Constructor
OuterClass Copy
InnerClass Constructor
innerClass() method
Hello!
InnerClass Destructor
2nd try:
InnerClass Constructor
innerClass() method
InnerClass Constructor
innerClass() method
InnerClass Assignment
InnerClass Destructor
InnerClass Destructor
OuterClass Destructor
OuterClass Destructor
After some research I read that there is no guarantee that the compiler will use the explicitely defined copy constructor. I do not understand this behavior. Why does the copy constructor even exist then, if we do not know that it is called? How does the compiler decide if it uses it?
Or, even better, is there a way to force the compiler to use the self-defined copy constructor?
Just for completeness with the other answers, the standard allows the compiler to omit the copy constructor in certain situations (what other answers refer to as "Return Value Optimization" or "Named Return Value Optimization" - RVO/NRVO):
12.8 Copying class objects, paragraph 15 (C++98)
Whenever a temporary class object is copied using a copy constructor, and this object and the copy have the same cv-unqualified type, an implementation is permitted to treat the original and the copy as two different ways of referring to the same object and not perform a copy at all, even if the class copy constructor or destructor have side effects. For a function with a class return type, if the expression in the return statement is the name of a local object, and the cv-unqualified type of the local object is the same as the function return type, an implementation is permitted to omit creating the temporary object to hold the function return value, even if the class copy constructor or destructor has side effects. In these cases, the object is destroyed at the later of times when the original and the copy would have been destroyed without the optimization.
So in your innerClass() method, the copy constructor you might think would be called at the return is permitted to be optimized away:
InnerClass innerClass() {
InnerClass ic;
std::cout << "innerClass() method" << std::endl;
return ic; // this might not call copy ctor
}
I agree with Neil, you should not write a class which depends on the copy constructor being called. Namely because the compiler can do things like "Named Return Value Optimization" (link) which completely avoids the copy constructor in many return value scenarios. In order to enforce calling the copy constructor you'd need to write a lot of code aimed at "tricking" the C++ compiler. Not a good idea.
In a particular scenario though if you want to enforce calling the copy constructor, you can do an explicit call.
Object SomeFunc() {
Object o1 = ...
return Object(o1);
}
You should not design your class with a reliance on the copy constructor being called (or not called) in specific circumstances. The compiler is allowed to elide or add copy constructor calls at all sorts of places, and you really don't want to have to keep track of them.
As for why you need one - well, the compiler may decide it needs a copy, and the copy constructor is what it uses to do so. The advantage of copy constructor calls being elided is performance - copying is usually quite an expensive operation.
Is this the problem ?
OuterClass(const OuterClass & rhs)
{
std::cout << "OuterClass Constructor" << std::endl;
==>
std::cout << "OuterClass Copy Constructor" << std::endl;
}
OuterClass & operator=(const OuterClass & rhs)
{
std::cout << "OuterClass Constructor" << std::endl;
==>
std::cout << "OuterClass Assignment operator" << std::endl;
}
A copy paste error!
I would suggest you to debug the code once to see what exactly is happening. Debugging really helps you to find the problems.
EDIT: for inner class issue:
As others have already pointed out this is the case of The Name Return Value Optimization(NRVO).
InnerClass innerClass()
{
InnerClass ic;
std::cout << "innerClass() method" << std::endl;
return ic;
}
the compiler can transforms the function to
void innerClass( InnerClass &namedResult)
{
std::cout << "innerClass() method" << std::endl;
}
Thus it eliminates both the return by value of the class object and the need to invoke the class copy constructor.
Please go through below two links to understand more on NRVO:
MSDN NRVO
Stan Lippman's BLog
Object o1(); doesn't create any objects rather it defines a function prototype with function name o1, void arguments and return type as Object. You need to post some code to find the actual problem.
Post some code. I think you are using some incorrect syntax.
The copy constructor must exactly have the following signature:
MyObject(const MyObject&)
Then you can see if the copy constructor is called with the following code:
MyObject m1;
MyObject m2(m1);
You are not allowed to use MyObject m1(); that is a function declaration.