I'm working in an old code where a pointer of an object of type A is passed to a function:
void fun(A* a)
{
if (dynamic_cast<const B*>(a) != NULL)
{
// Use B object
}
else
{
// Use C object
}
}
The classes B and C inherit from A and they kind of used dynamic_cast to test the input type (actually to test if "castable"). Which seems wrong.
I tried using std::is_same but I may be doing something wrong:
if(std::is_same<decltype(*a), A>::value ) {
std::cout << "Equal" << std::endl;
}
else
{
std::cout << "Not equal" << std::endl;
std::cout << typeid(*a).name() << std::endl; // This
std::cout << typeid(A).name() << std::endl; // And this prints the same
}
I always get into the "Not equal" case event if the following line print the same.
Am I doing something wrong with std::is_same ?
Is there another (better) way?
Related
The following code produces the following output and ends up in kind of an endless loop with 100% cpu load.
#include <iostream>
#include <set>
class Foo{};
void delete_object_from_set(std::set<Foo *>& my_set, Foo* ob)
{
std::set< Foo *>::iterator setIt;
std::cout << "Try to delete object '" << ob << "'..." << std::endl;
for(setIt = my_set.begin(); setIt != my_set.end(); ++setIt)
{
Foo * tmp_ob = *setIt;
std::cout << "Check object '" << tmp_ob << "'..." << std::endl;
// compare the objects
//
if(ob == tmp_ob)
{
// if the objects are equal, delete this object from the set
//
std::cout << "Delete object '" << tmp_ob << " from set..." << std::endl;
setIt = my_set.erase(setIt);
std::cout << "Deleted object '" << tmp_ob << " from set..." << std::endl;
}
}
std::cout << "loop finished." << std::endl;
};
int main()
{
Foo* ob = new Foo();
std::set< Foo * > my_set;
my_set.insert(ob);
delete_object_from_set(my_set, ob);
std::cout << "finished" << std::endl;
return 0;
}
The output:
Try to delete object '0x563811ffce70'...
Check object '0x563811ffce70'...
Delete object '0x563811ffce70 from set...
Deleted object '0x563811ffce70 from set...
so it does not finish, having 100% cpu load.
I know how to do it correctly (see below), but I cannot understand what is going on here. It's not an endless loop, since then it should output something continuously, but it just keeps doing something. Any idea what?
How to do it the right way: Deleting elements from std::set while iterating and How to remove elements from an std::set while iterating over it
Hokay, so you asked how this could loop infinitely without continuously triggering the "Check object" print.
The quick answer (that you already got from others) is that calling operator++ on my_set.end() is UB, and thus able to do anything.
A deeper dive into GCC specifically (since #appleapple could reproduce on GCC, while my test in MSVC found no infinite loop) revealed some interesting stuff:
The operator++ call is implemented as a call to _M_node = _Rb_tree_increment(_M_node); and that one looks as follows:
static _Rb_tree_node_base*
local_Rb_tree_increment(_Rb_tree_node_base* __x) throw ()
{
if (__x->_M_right != 0)
{
__x = __x->_M_right;
while (__x->_M_left != 0)
__x = __x->_M_left;
}
else
{
_Rb_tree_node_base* __y = __x->_M_parent;
while (__x == __y->_M_right)
{
__x = __y;
__y = __y->_M_parent;
}
if (__x->_M_right != __y)
__x = __y;
}
return __x;
}
So, it defaults to finding the "next" node by taking the first right, and then running all the way to the left. But! a look in the debugger at the my_set.end() node reveals the following:
(gdb) s
366 _M_node = _Rb_tree_increment(_M_node);
(gdb) p _M_node
$1 = (std::_Rb_tree_const_iterator<Foo*>::_Base_ptr) 0x7fffffffe2b8
(gdb) p _M_node->_M_right
$2 = (std::_Rb_tree_node_base::_Base_ptr) 0x7fffffffe2b8
(gdb) p _M_node->_M_left
$3 = (std::_Rb_tree_node_base::_Base_ptr) 0x7fffffffe2b8
Both the left and right of the end() node apparently points at itself. Why? Ask the implementer, but probably because it makes something else easier or more optimizable. But it does mean that in your case the UB you run into is an infinite loop on essentially:
__x->_M_left = __x;
while (__x->_M_left != 0)
__x = __x->_M_left; // __x = __x;
Again, this is the case for GCC, on MSVC it did not loop (debug threw an exception, release just ignored it; finished the loop and printed "loop finished." and "finished" as if nothing strange had happened). But that is the "fun" part about UB - anything could happen...
The code is equivalent of:
{
setIt = my_set.begin();
while(setIt != my_set.end())
{
Foo * tmp_ob = *setIt;
std::cout << "Check object '" << tmp_ob << "'..." << std::endl;
if(ob == tmp_ob)
{
std::cout << "Delete object '" << tmp_ob << " from set..." << std::endl;
setIt = my_set.erase(setIt);
std::cout << "Deleted object '" << tmp_ob << " from set..." << std::endl;
}
++setIt;
}
}
The call to my_set.erase(setIt); may return end() if last element of container was deleted. Consequently increments happens, which is UB. Would it trigger exception or not depends on implementation, but at any following point setIt never will be equal to my_set.end(), thus an infinite loop is possible.
for(setIt = my_set.begin(); setIt != my_set.end();)
{
Foo * tmp_ob = *setIt;
std::cout << "Check object '" << tmp_ob << "'..." << std::endl;
// compare the objects
//
if(ob == tmp_ob)
{
// if the objects are equal, delete this object from the set
//
std::cout << "Delete object '" << tmp_ob << " from set..." << std::endl;
setIt = my_set.erase(setIt);
std::cout << "Deleted object '" << tmp_ob << " from set..." << std::endl;
}
else
setIt++;
}
In question about scope of exception it is stated by Aj. that throw and catch clauses will create copies of the exception (unless reference is used I guess)
I tried myself a small toy code and I don't understand the result. here :
//g++ 7.4.0
#include <iostream>
using namespace std;
struct Some_error {
Some_error(float code):err_code(code){ cout << "Some_error(" << err_code << ")\n"; }
~Some_error() { cout << "~Some_error(" << err_code << ")\n"; }
Some_error(const Some_error& o):err_code(o.err_code+0.1) { cout << "Some_error(copy::" << err_code << ")\n"; }
Some_error(Some_error&& o):err_code(std::move(o.err_code)+.01){ cout << "Some_error(move::" << err_code << ")\n"; }
int get_code() const { return err_code; }
private : float err_code;
};
int do_task() {
if ( false ) return 42; else throw Some_error {1};
cout << "end do_task\n" ;
}
void taskmaster(){
try { auto result = do_task(); cout << "the answer is " << result << "\n" ; }
catch (Some_error e) { cout << "catch Some_error : " << e.get_code() << "\n" ; }
cout << "end taskmaster\n" ;
}
int main() { taskmaster(); }
the trace I get is as follows :
Some_error(1)
Some_error(copy::1.1)
catch Some_error : 1
~Some_error(1.1)
~Some_error(1)
end taskmaster
Now first, as I used no reference here, according to Aj., I would expect 2 copies to happen.
And second, there was a copy, that set err_code to 1.1, but the display is still 1.
Remark: just to be complete, I changed the catch to : catch(Some_error& e),
and then the trace looks fine to me :
Some_error(1)
catch Some_error : 1
~Some_error(1)
end taskmaster
I would expect 2 copies to happen.
Why? Only one copy is made by the catch block. Where would the second copy happen?
set err_code to 1.1, but the display is still 1.
Because get_code returns an int, so the floating point value gets truncated.
This question already has answers here:
What are the differences between a pointer variable and a reference variable?
(44 answers)
Closed 9 years ago.
I have objects that I put into a std::vector. Later on I need to iterate through the vector and change some member variables in the objects in each position.
I think I want to pass the object once I have it by reference to a function to operate on it, but I seem to be getting an error:
Non-const lvalue reference to type 'Object' cannot bind to a value of unrelated type 'Object *'
Here is the general gist with code between omitted:
Object* o1 = Object::createWithLocation(p.x, p.y);
v.push_back(o1);
// later on
for (int f=0; f < v.size(); f++)
{
Object* obj1 = v.at(f);
addChild(h->createLayer(obj1), 3); // add the HUD
}
createLayer is defined at:
static PlantingHUD* createLayer(Object &o);
Can anyone explain my confusion between pointers and passing by reference? Do I have to do a cast of some sort?
static PlantingHUD* createLayer(Object &o);
this method need a reference to Object as the parameter,
but your input is a pointer.
Object* obj1 = v.at(f);
addChild(h->createLayer(obj1), 3); // add the HUD
That's the problem.
void foo(Object o)
Declares a function, foo, which will begin execution with a fresh, new, instance of class 'Object' called 'o'.
This is called "passing by value", but it's more accurately 'copying' because what foo receives is it's own, personal copy of the Object instances we call foo with. When "foo" ends, the "Object o" it knew, fed and put through school, will cease to be.
void foo(Object& o)
Declares a function, foo, which will begin executing with a reference to an existing instance of an 'Object', this reference will be called 'o'. If you poke or prod it, you will be changing the original.
This is called "pass by reference".
void foo(Object* o)
Declares a function, foo, which will begin executing with a variable, called "o", containing the address of what is supposed to be an instance of "Object". If you change this variable, by doing something like "o = nullptr", it will only affect the way things look inside foo. But if you send Samuel L Jackson to the address, he can deliver furious vengance that lasts beyond the lifetime of foo.
void foo(Object*& o)
Declares a function, foo, which will begin executing with a variable called "o", which is a reference to a pointer to an instance of object o - it's like an alias, except that without compiler optimization, it's actually implemented by the compiler using a sort of pointer.
Lets try these separately.
#include <iostream>
#include <cstdint>
struct Object
{
int m_i;
void event(const char* what, const char* where)
{
std::cout <<
what<< " " << (void*)this <<
" value " << m_i <<
" via " << where <<
std::endl;
}
// Construct an object with a specific value.
Object(int i) : m_i(i)
{
event("Constructed", "Operator(int i)");
}
// This is called the copy constructor, create one object from another.
Object(const Object& rhs) : m_i(rhs.m_i)
{
event("Constructed", "Operator(const Object&)");
}
// This is how to handle Object o1, o2; o1 = o2;
Object& operator=(const Object& rhs)
{
m_i = rhs.m_i;
event("Assigned", "operator=");
return *this;
}
// Handle destruction of an instance.
~Object() { event("Destructed", "~Object"); }
};
void foo1(Object o)
{
std::cout << "Entered foo1, my o has value " << o.m_i << std::endl;
// poke our local o
o.m_i += 42;
std::cout << "I changed o.m_i, it is " << o.m_i << std::endl;
}
void foo2(Object* o)
{
std::cout << "Foo2 starts with a pointer, it's value is " << (uintptr_t)o << std::endl;
std::cout << "That's an address: " << (void*)o << std::endl;
std::cout << "m_i of o has the value " << o->m_i << std::endl;
o->m_i += 42;
std::cout << "I've changed it tho, now it's " << o->m_i << std::endl;
}
void foo3(Object& o)
{
std::cout << "foo3 begins with a reference called o, " << std::endl <<
"which is sort of like a pointer but the compiler does some magic " << std::endl <<
"and we can use it like a local concrete object. " <<
std::endl <<
"Right now o.m_i is " << o.m_i <<
std::endl;
o.m_i += 42;
std::cout << "Only now, it is " << o.m_i << std::endl;
}
void foo4(Object*& o)
{
std::cout << "foo4 begins with a reference to a pointer, " << std::endl <<
"the pointer has the value " << (uintptr_t)o << " which is " <<
(void*)o <<
std::endl <<
"But the pointer points to an Object with m_i of " << o->m_i << std::endl <<
"which we accessed with '->' because the reference is to a pointer, " <<
"not to an Object." <<
std::endl;
o->m_i += 42;
std::cout << "I poked o's m_i and now it is " << o->m_i << std::endl;
// Now for something really dastardly.
o = new Object(999);
std::cout << "I just changed the local o to point to a new object, " <<
(uintptr_t)o << " or " << (void*)o << " with m_i " << o->m_i <<
std::endl;
}
int main()
{
std::cout << "Creating our first objects." << std::endl;
Object o1(100), o2(200);
std::cout << "Calling foo1 with o1" << std::endl;
foo1(o1);
std::cout << "back in main, o1.m_i is " << o1.m_i << std::endl;
std::cout << "Calling foo2 with &o1" << std::endl;
foo2(&o1);
std::cout << "back in main, o1.m_i is " << o1.m_i << std::endl;
std::cout << "Calling foo3(o2), which looks like the way we called foo1." << std::endl;
foo3(o2);
std::cout << "back in main, o2.m_i is " << o2.m_i << std::endl;
std::cout << "Creating our pointer." << std::endl;
Object* optr;
std::cout << "Setting it to point to 'o2'" << std::endl;
optr = &o2;
std::cout << "optr now has the value " << (uintptr_t)optr <<
" which is the address " << (void*)optr <<
" which points to an Object with m_i = " << optr->m_i <<
std::endl;
foo4(optr);
std::cout << "back in main, o2 has the value " << o2.m_i << std::endl <<
"and now optr has the value " << (uintptr_t)optr << std::endl <<
"and optr->m_i is now " << optr->m_i <<
std::endl;
if (optr != &o2)
delete optr; // otherwise we'd technically be leaking memory.
return 0;
}
Live demo on ideone.com.
Passing by Value
This term confuses people early in their C++ development because, in lay terms, it sounds like this is what "Object& foo" would do.
The term "pass by value" actually arises from what the language has to do to call such a function, to value-wise copy the whole of the original object/struct onto the stack or, in the case where a copy ctor is available, forward them to a value-wise constructor and recreate a copy of the original, value-by-value.
Pass-by-value should be used for most simple cases where you do not want side-effects on the values in your current scope from the function you are calling.
bool checkWidthdrawl(Dollars balance, Dollars amountToWithdraw)
{
// it's safe for me to change "balance" here because balance is mine
}
vs
bool checkWidthdrawl(Dollars& balance, Dollars amountToWithdraw)
{
balance -= amountToWithdraw;
if (balance < 0)
std::complaint << "My account seems to be missing $" << amountToWithdraw;
}
However, passing by reference can become expensive.
struct FourK { char a[1024], b[1024], c[1024], d[1024]; }
If you pass this around by value all day, you risk blowing up your stack at some point, as well as spending daft amounts of time copying all those bytes.
void foo(int i); // Unless you need to see the changes to i, this is perfectly fine.
void foo(FourK f); // Someone should hunt you down and yell "PEANUT" in your ear.
Passing by reference
References are really a contract over the pointer system that allow the language to ensure you're really talking about a concrete instance of an object, and thus allow you to refer to a pre-existing instance of a value outside of a function.
Of course, there are ways to break this, but the language tries very, very hard to make them difficult to do. For example, try adding this to the above code:
Object& makeObjectNotWar(int i)
{
Object thisObjectGoesAway(i);
return thisObjectGoesAway /*right about now*/;
}
You can also provide callers with an assurance that the function won't have any side effects on a variable with the "const" modifier.
void fooc(const Object& o)
{
o.m_i += 42; // Error
}
You can even use that within a function as a hint to yourself (and the compiler) that you don't want to accidentally change a value, here's a case where it can provide an optimization hint to the compiler:
std::vector<int> foo;
add1000valuesTo(foo);
const size_t fooSize = foo.size();
for (size_t i = 0; i < fooSize; ++i) {
// ... stuff you're sure won't decrease foo.size()
}
Without the const fooSize
for (size_t i = 0; i < foo.size(); ++i) {
The compiler has to start by assuming that "foo.size()" could be changed at any given iteration of the loop. It can probably figure out that it doesn't, but by giving it the hint, you've saved a little compile time, possibly improved your performance, and made it easier for a human to tell exactly what behavior you expected. Downside: If your loop does actually change the size of foo, you'll find out by bug reports :(
One last thing to know about pass-by-reference is that C++ references aren't protected or "ref counted". The language only promises that a reference will be valid for the duration of its scope, so long as you don't do anything stupid like, say, call something that deletes the object.
// Author intended this function to be called
// by the owner of a Dog.
void doneWithFoo(Dog& dog)
{
Dog* deadDog = &dog;
delete deadDog;
}
Rover& Babysitter::babysitDog(Dog& rover, int hours)
{
rover.feed(FeedType::Donut);
if (rover.pooped())
doneWithDog(rover);
// ...
return rover; // I have a bad feeling about this.
}
Obviously, you're not expecting "babysitDog" to result in the dog being disposed of. But bear in mind that because we passed in a reference, it to "babysitDog" that it's also gone from the caller too, and if that was using a reference... rover's dead, Dave, dead.
As with pointers, if you're going to store references beyond the scope in which you have access to them, then you become responsible for making sure the objects being referenced stick around or that the references are removed from the container before the objects do go away.
I wrote a very simple solution however someone laughed and found a flaw as shown here http://ideone.com/IcWMEf
#include <iostream>
#include <ostream>
#include <functional>
#include <exception>
using namespace std;
// Wrong scope(failure)
class FailBlockT
{
typedef function<void()> T;
public:
T t;
FailBlockT(T t)
{
this->t=t;
}
~FailBlockT()
{
if (std::uncaught_exception())
{
t();
}
}
};
struct Test
{
~Test()
{
try
{
FailBlockT f([]()
{
cout << "failure" << endl;
});
// there is no any exception here, but "failure" is printed.
// See output below
}
catch(...)
{
cout << "some exception" << endl;
}
}
};
int main()
{
try
{
Test t;
throw 1;
}
catch(int){}
return 0;
}
In short the problem is my code looks at std::uncaught_exception(). When an exception is thrown and a normal destructor is executed. If i use scope failure there it will look at std::uncaught_exception() and think the object scope is lost due to exception rather then simply walking out of scope.
I can't think of any good solutions to differentiate leaving scope normally VS having an exception thrown IN it. Yes i know throwing is a bad idea in dtors BUT thats why I fail to notice this problem, because I never throw in exceptions.
How do I differentiate/solve this?
No exception was thrown but it thinks it has.
An exception was thrown, just not from right there.
There is no mechanism in C++11 to ask, "Was an exception thrown from code just below me, but not from code elsewhere in the call-stack?" std::uncaught_exception is doing exactly what it is supposed to do: say whether there is an exception currently in the process of being resolved at the time the function is called. And there is, so it returns true.
C++17 adds std::uncaught_exceptions (note the plural), which can be used to detect the difference. With such a tool, you can make your FailBlock object work:
template<typename Func>
class FailBlockT
{
private:
int e_count_;
T t_;
public:
FailBlockT(T t) : e_count_(std::uncaught_exceptions()), t_(t) {}
FailBlock(const FailBlock &) = delete; //The type should not be mobile.
~FailBlockT()
{
if (std::uncaught_exceptions() != e_count_)
{
t_();
}
}
};
std::uncaught_exceptions() returns the number of exceptions that are provoking stack unwinding at the time the call was made. If the number is the same during the constructor and destructor of an object (assuming it's a stack object), then the destructor is not being called due to an exception being thrown through where this type was used.
But without this tool, it, there's not much you can do to differentiate between an exception provoking the exiting of the scope rather than exiting a scope when exception unwinding just happens to be going on. So you're going to have to bite the bullet and catch the exception like everyone else.
Or just don't put this FailBlock thing in destructors. It seems to me that those should go directly into regular functions that can actually throw (and destructors should never throw). It seems to me that you're worried about a corner case that doesn't make any real sense.
I can't think of any good solutions to differentiate leaving scope normally VS having an exception thrown IN it.
Check stack_unwinding library - I have implemented scope(failure) and scope(success) features in C++.
It is based on platform specific function uncaught_exception_count. It is similar to std::uncaught_exception from standard library, but instead of boolean result it returns unsigned int showing current count of uncaught exceptions.
Currently it is tested on {Clang 3.2, GCC 3.4.6, GCC 4.1.2, GCC 4.4.6, GCC 4.4.7, MSVC2005SP1, MSVC2008SP1, MSVC2010SP1, MSVC2012} x {x32, x64}.
In C++11 folowing syntax is available:
try
{
int some_var=1;
cout << "Case #1: stack unwinding" << endl;
scope(exit)
{
cout << "exit " << some_var << endl;
++some_var;
};
scope(failure)
{
cout << "failure " << some_var << endl;
++some_var;
};
scope(success)
{
cout << "success " << some_var << endl;
++some_var;
};
throw 1;
} catch(int){}
{
int some_var=1;
cout << "Case #2: normal exit" << endl;
scope(exit)
{
cout << "exit " << some_var << endl;
++some_var;
};
scope(failure)
{
cout << "failure " << some_var << endl;
++some_var;
};
scope(success)
{
cout << "success " << some_var << endl;
++some_var;
};
}
In C++98 it is a bit more noisier:
try
{
cout << "Case #1: stack unwinding" << endl;
BOOST_SCOPE_EXIT(void) { cout << "exit" << endl; } BOOST_SCOPE_EXIT_END
SCOPE_FAILURE(void) { cout << "failure" << endl; } SCOPE_FAILURE_END
SCOPE_SUCCESS(void) { cout << "success" << endl; } SCOPE_SUCCESS_END
throw 1;
} catch(int){}
{
cout << "Case #2: normal exit" << endl;
BOOST_SCOPE_EXIT(void) { cout << "exit" << endl; } BOOST_SCOPE_EXIT_END
SCOPE_FAILURE(void) { cout << "failure" << endl; } SCOPE_FAILURE_END
SCOPE_SUCCESS(void) { cout << "success" << endl; } SCOPE_SUCCESS_END
}
Also, library has UNWINDING_AWARE_DESTRUCTOR feature. Example:
struct DestructorInClass
{
UNWINDING_AWARE_DESTRUCTOR(DestructorInClass,unwinding)
{
cout << "DestructorInClass, unwinding: "
<< ( unwinding ? "true" : "false" ) << endl;
}
};
However, there are some cases where UNWINDING_AWARE_DESTRUCTOR may give wrong results (though scope(success) and scope(failure) features are not affected by such issues).
I was reading Want Speed? Pass by Value on the C++ Next blog and created this program to get a feel for copy elision and move semantics in C++0x:
#include <vector>
#include <iostream>
class MoveableClass {
public:
MoveableClass() : m_simpleData(0), instance(++Instances) {
std::cout << "Construct instance " << instance << " (no data)" << std::endl;
}
MoveableClass(std::vector<double> data) : m_data(std::move(data)), m_simpleData(0), instance(++Instances) {
std::cout << "Construct instance " << instance << " (with data)" << std::endl;
}
MoveableClass(int simpleData) : m_simpleData(simpleData), instance(++Instances) {
std::cout << "Construct instance " << instance << " (with simple data)" << std::endl;
}
MoveableClass(const MoveableClass& other)
: m_data(other.m_data), m_simpleData(other.m_simpleData), instance(++Instances)
{
std::cout << "Construct instance " << instance << " from a copy of " << other.instance << std::endl;
Elided = false;
}
MoveableClass(MoveableClass&& other)
: m_data(std::move(other.m_data)), m_simpleData(other.m_simpleData), instance(++Instances)
{
std::cout << "Construct instance " << instance << " from a move of " << other.instance << std::endl;
Elided = false;
}
MoveableClass& operator=(MoveableClass other) {
std::cout << "Assign to instance " << instance << " from " << other.instance << std::endl;
other.Swap(*this);
return *this;
}
~MoveableClass() {
std::cout << "Destroy instance " << instance << std::endl;
--Instances;
}
void Swap(MoveableClass& other) {
std::swap(m_data, other.m_data);
std::swap(m_simpleData, other.m_simpleData);
}
static int Instances;
static bool Elided;
private:
int instance;
int m_simpleData;
std::vector<double> m_data;
};
int MoveableClass::Instances = 0;
bool MoveableClass::Elided = true;
std::vector<double> BunchOfData() {
return std::vector<double>(9999999);
}
int SimpleData() {
return 9999999;
}
MoveableClass CreateRVO() {
return MoveableClass(BunchOfData());
}
MoveableClass CreateNRVO() {
MoveableClass named(BunchOfData());
return named;
}
MoveableClass CreateRVO_Simple() {
return MoveableClass(SimpleData());
}
MoveableClass CreateNRVO_Simple() {
MoveableClass named(SimpleData());
return named;
}
int main(int argc, char* argv[]) {
std::cout << "\nMove assign from RVO: " << '\n';
{
MoveableClass a;
a = CreateRVO();
}
std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
MoveableClass::Elided = true; // reset for next test
std::cout << "\nMove assign from RVO simple: " << '\n';
{
MoveableClass a;
a = CreateRVO_Simple();
}
std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
MoveableClass::Elided = true; // reset for next test
std::cout << "\nMove assign from NRVO: " << '\n';
{
MoveableClass a;
a = CreateNRVO();
}
std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
MoveableClass::Elided = true; // reset for next test
std::cout << "\nMove assign from NRVO simple: " << std::endl;
{
MoveableClass a;
a = CreateNRVO_Simple();
}
std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
MoveableClass::Elided = true; // reset for next test
}
Here is the output I get when compiled in release mode on Visual C++ 10.0 (Beta 2):
Move assign from RVO:
Construct instance 1 (no data)
Construct instance 2 (with data)
Construct instance 3 from a move of 2
Destroy instance 2
Assign to instance 1 from 3
Destroy instance 3
Destroy instance 1
Move elided: No
Move assign from RVO simple:
Construct instance 1 (no data)
Construct instance 2 (with simple data)
Assign to instance 1 from 2
Destroy instance 2
Destroy instance 1
Move elided: Yes
Move assign from NRVO:
Construct instance 1 (no data)
Construct instance 2 (with data)
Assign to instance 1 from 2
Destroy instance 2
Destroy instance 1
Move elided: Yes
Move assign from NRVO simple:
Construct instance 1 (no data)
Construct instance 2 (with simple data)
Assign to instance 1 from 2
Destroy instance 2
Destroy instance 1
Move elided: Yes
However, I am perplexed by one thing. As you can see, all of the moves are elided except for the first one. Why can't the compiler perform RVO with a MoveableClass(std::vector) at line 86, but can with a MoveableClass(int) at line 97? Is this just a bug with MSVC or is there a good reason for this? And if there is a good reason, why can it still perform NRVO on a MoveableClass(std::vector) at line 91?
I'd like to understand it so I can go to sleep happy. :)
Thanks for replying Dave.
I've added my tests to that example:
pastebin.com/f7c8ca0d6
Curiously it shows that all types of elisions are not being performed except for NRVO!
Edit: Actually I suppose this is because it is the only test where the object ever has a name.
I also tried other STL types and got the same result. However when trying my own non-pod types it works as expected. I can't think what's special about the STL types that could be causing this so I don't know what else to try.
I'll submit a bug report.
Edit: Submitted here
Thanks
Hmm.
It seems that if you change the data constructor
MoveableClass::MoveableClass(std::vector<double> data)
to accept the vector by reference, like so,
MoveableClass::MoveableClass(const std::vector<double>& data)
it works fine! Why does it not work if you pass the vector by value?
Also here's a version that should compile on earlier versions of MSVC, if anybody wants to run the test there. It contains no C++0x features: http://pastebin.com/f3bcb6ed1
Maybe it'd be a good idea to update and maintain this example from cpp-next with a version of your test that fails, so there can be one comprehensive, canonical test.