I realy dont know what could cause all that things to happen, as it seems it just copys and moves a bit, not a lot, but THAT...!
BTW: I realise it prints just 1, not the real value! why?
template<typename T>
class foo
{
T a;
public:
foo()
{
a=T();
cout<<"default constructed"<<endl;
}
};
This tells the compiler
1: call T's default constructor to initialize a
2: call T's default constructor again to initialize a temporary
3: call T's operator= to trash the value you just stored in a, and move over the contents of the temprary
4. call T's destructor
5: output "default constructed".
Start with writing constructors correctly.
There is also an ambiguity error with "a!=T(0)" which clang++ points out.
Also, the constructor taking an rvalue reference to T should be moving x. You're just copying it and pointlessly erasing the old value
here's an example from a tutorial how to make a move constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// Move constructor.
MemoryBlock(MemoryBlock&& other)
: _data(NULL)
, _length(0)
{
std::cout << "In MemoryBlock(MemoryBlock&&). length = "
<< other._length << ". Moving resource." << std::endl;
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length; //1.
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = NULL;
other._length = 0; //2.
}
(I added the 1. and 2.)
1.: COPYING!
2.: ERASING THE OLD VALUE!
OK, as I added im my original post, it printed the wrong value. I menaged to fix it, but (of course) it added a lot more lines to the output!
The new foo class:
For some reason the int() operator was calling the bool() operator. Anyway I fixed it, and unfortunatly added more output. Again, why?
and, does anyone know how to make improvements?
BTW, does std::move also sat the value to the default? That would be awsome.
foo<int> is a type.
foo<foo<int>> is a type. This type requires the type foo<int> to exist.
foo<foo<foo<int>>> is a type. This type requires foo<int> and foo<foo<int>> to exist.
ad nauseum.
So when you instantiate a type of foo<foo<int>> you're instantiating an object of type foo<int> and an object of type foo<foo<int>>. When you instantiate an object of type foo<foo<foo<int>>> you're instantiating an object of type foo<int>, an object of type foo<foo<int>> and an object of type foo<foo<foo<int>>>. ad, nauseum.
and, does anyone know how to make improvements?
Avoid doing this as much as possible, and work on your understanding of move semantics.
Here's your code, corrected to avoid generating so many temporaries with a different driver:
#include <iostream>
usingnamespace std ;
template<typename T>
class foo
{
T a;
public:
foo() : a()
{
cout<<"default constructed"<<endl;
}
foo(const foo& x) : a(x.a)
{
cout<<"copy constructed from object"<<endl;
}
foo(foo&& x) : a(std::move(x.a))
{
cout << "move-constructed from object\n" ;
}
foo(const T& x) : a(x)
{
cout<<"copy constructed from value"<<endl;
}
foo(T&& x) : a(std::move(x))
{
cout<<"move constructed from value"<<endl;
}
~foo()
{
cout<<"destructor called..."<<endl;
}
foo& operator=(const foo& x)
{
a=x.a;
cout<<"copy assignment"<<endl;
return *this;
}
foo& operator=(foo&& x)
{
a= std::move(x.a);
cout<<"move assignment"<<endl;
return *this;
}
};
int main()
{
// if you count the number of "foo" in the "same as" comments you will
// find it corresponds to the number of objects created.
{
cout << "foo<int> a(10): \n" ;
foo<int> a(10) ;
}
{ // implicit temporary here. Same as: foo<foo<int>> b(foo<int>(10))
cout << "\n\nfoo<foo<int>> b(10): \n" ;
foo<foo<int>> b(10) ;
}
// when we get this far, we have to explicitly generate temporaries
// to supply an argument to the constructor, because implicit temporaries
// will only get you so far.
// same as foo<foo<foo<int>>> c(foo<foo<int>(foo<int>(10))
{
cout << "\n\nfoo<foo<foo<int>>> c(foo<int>(10)): \n" ;
foo<foo<foo<int>>> c(foo<int>(10)) ;
}
{ // same as foo<foo<foo<foo<int>>>> d(foo<foo<foo<int>>>>(foo<foo<int>>(foo<int>(10))))
cout << "\n\nfoo<foo<foo<foo<int>>>> d(foo<foo<int>>(10)): \n" ;
foo<foo<foo<foo<int>>>> d(foo<foo<int>>(10)) ;
}
}
foo<int> a(10):
move constructed from value
destructor called...
foo<foo<int>> b(10):
move constructed from value
move-constructed from object
move constructed from value
destructor called...
destructor called...
destructor called...
foo<foo<foo<int>>> c(foo<int>(10)):
move constructed from value
move-constructed from object
move constructed from value
move-constructed from object
move-constructed from object
move constructed from value
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
foo<foo<foo<foo<int>>>> d(foo<foo<int>>(10)):
move constructed from value
move-constructed from object
move constructed from value
move-constructed from object
move-constructed from object
move constructed from value
move-constructed from object
move-constructed from object
move-constructed from object
move constructed from value
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
destructor called...
Press any key to continue . . .
You can see how injecting unneeded temporaries into this can make it look much worse.
Your move constructor 'example' was dealing with raw pointers and ownership. Your code does not.
Just a small clarification, hopefully. std::move does nothing at all. It does not compile into any code.
It is only if you use std::move() as an argument in a function call (including operators), and that function has both lvalue-reference and rvalue-reference overloads, std::move tells the compiler to call to the rvalue overload of the function.