Reputation: 11885
Right when I thought I understood what std::move and move constructors do, I tried to write some unit test, actually testing the move constructor for some class...
To my surprise I found, that I cannot think of a way to construct code which actually calls the move constructor. Worse, I cannot even set a breakpoint in the body of the move constructor (in VS2013 community edition, debug, 64bit build).
Wondering if this is a compiler peculiarity, I knocked up some small test code on my freebsd virtual machine, using clang (3.4.1). There, too I fail to find a way to get that move constructor invoked.
#include <iostream>
#include <stdint.h>
#include <string>
#include <algorithm>
#include <functional>
#include <ctype.h>
#include <locale>
void InPlaceToUpper( std::string& target )
{
std::transform(target.begin(), target.end(), target.begin(), ::toupper);
}
void InPlaceToLower( std::string& target )
{
std::transform(target.begin(), target.end(), target.begin(), ::tolower);
}
std::string ToUpper( const std::string& s )
{
std::string result;
result.resize(s.length());
std::transform(s.begin(), s.end(), result.begin(), ::toupper);
return result;
}
std::string ToLower( const std::string& s)
{
std::string result;
result.resize(s.length());
std::transform(s.begin(), s.end(), result.begin(), ::tolower);
return result;
}
class CFoo
{
std::string m_value;
public:
CFoo()
: m_value()
{
std::cout << "CFoo() called." << std::endl;
}
CFoo(const char *value)
: m_value(value)
{
std::cout << "CFoo(const char *) called." << std::endl;
}
CFoo(const std::string& value )
: m_value(value)
{
std::cout << "CFoo(const std::string&) called." << std::endl;
}
CFoo(const CFoo& other )
: m_value(other.m_value)
{
std::cout << "CFoo() copy constructor called." << std::endl;
}
CFoo(CFoo&& other )
: m_value(std::move(other.m_value))
{
std::cout << "CFoo() move constructor called." << std::endl;
std::cout << "other.m_value = " << other.m_value.c_str() << std::endl;
}
~CFoo()
{
std::cout << "~CFoo() called." << std::endl;
}
const CFoo& operator=( const CFoo& other )
{
std::cout << "CFoo copy assignment operator called." << std::endl;
if( &other != this )
{
m_value = other.m_value;
}
return *this;
}
const CFoo& operator=( CFoo&& other )
{
std::cout << "CFoo move assignment operator called." << std::endl;
if( &other != this )
{
m_value = std::move(other.m_value);
}
return *this;
}
CFoo ToUpper()
{
return CFoo(::ToUpper(m_value));
}
CFoo ToLower()
{
return CFoo(::ToLower(m_value));
}
const char * ToString() const
{
return m_value.c_str();
}
};
int main( int argc, const char *argv[] )
{
{
CFoo foo;
CFoo foo1("Hello World");
CFoo foo2 = CFoo("Hello again World!");
CFoo foo3(CFoo("Bye world"));
CFoo foo4 = CFoo("Bye again world");
CFoo foo5 = foo4.ToUpper();
CFoo foo6 = foo4.ToLower();
foo6 = foo4.ToUpper();
std::cout << "foo4: " << foo4.ToString() << std::endl;
foo6 = CFoo("Well well well");
}
return 0;
}
My apologies if the code is not as short as it might possibly be. But there are only a few spots to look at, namely my efforts to get the move constructor invoked in main() and the definition of the various constructors in class Foo.
I am aware of compiler settings which allow turning off RVO and stuff but for the purpose of using the feature "move constructor" in performance aware code, there should be a classic example of when it gets invoked. If that is not the case, I will probably decide not to even bother using move constructors at all.
To answer the question, you can tell me a line I can write in main() which gets the move constructor of CFoo called. Or you can tell me what I am doing wrong.
Does std::string support being moved like that? Maybe this is why my efforts fail?
Thanks, in advance.
Upvotes: 3
Views: 668
Reputation: 171293
In all your attempts to use the move constructor it is being elided, so that e.g. CFoo foo = CFoo(blah);
is simply equivalent to CFoo foo(blah);
which doesn't need to use the move constructor. This is a Good Thing because the compiler is optimising away the need for any copy or move to happen at all.
To see the move constructor being used try:
CFoo f1;
CFoo f2 = std::move(f1);
This constructs f2
from an rvalue, and nothing can be elided, so the move constructor will be used.
Upvotes: 8
Reputation: 5354
First of all, there is an error in std::string ToUpper(const std::string& s)
, namely there is no space in result
. C++ algorithms do not grow their target containers automatically. You must either allocate space yourself, or use a inserter adaptor.
To make space in out, e.g. do this:
result.resize(s.length());
After it the move assignment operator called for:
foo6 = foo4.ToUpper();
foo6 = CFoo("Well well well");
The move constructor is called whenever an object is initialized from xvalue of the same type, which includes:
T a = std::move(b);
or T a(std::move(b));
, where b
is of type T
f(std::move(a));
, where a
is of type T
and f
is void f(T t)
return a;
inside a function such as T f()
, where a
is of type T
which has a move constructor.For more see move constructors on cppreference.com.
Upvotes: 1