3 ms·
I find this stuff frustrating because it's so hard to figure out if I'm implementing it properly. I can't rely on trial and error, or compiler verification, bec
by ridiculous_fish 12y ago
I find this stuff frustrating because it's so hard to figure out if I'm implementing it properly. I can't rely on trial and error, or compiler verification, because errors manifest as extra copying or other inefficiencies, instead of observably wrong behavior.
In fact it's often hard to even figure out what's going on at all. For example, in the MyKlass case.
v.push_back(MyKlass(2, 3.14f));
What's being invoked: a move constructor or copy constructor? How would you verify? You could maybe add a move or copy constructor with some logging, but defining either of those can change which one gets invoked! So I guess you have to hunt through the assembly.
Here's another case that terrifies me. Testing with clang 3.5:
return a; // invokes a's move constructor
return true ? a : a; // invokes a's copy constructor
if (true) return a; else return a; // elides constructors using RVO
So even when you do get it right, seemingly irrelevant changes can silently break it. It's so fragile!
How do other C++ programmers verify that they're getting this stuff right?
- stinos 12y agoHow do other C++ programmers verify that they're getting this stuff right? I shared your frustation then recognized it's origin: me not properly understanding the stuff because of lack of knowledge and training. So I spent a couple of hours searching the internet, writing some code and verifying behavior (1) until I knew what I needed to know about the subject. Then applied that knowlegde to some heavy template code using all new features (variadic templates/perfect forwarding/...). Figured I still didn't know enough so I learnt some more. In the end it took a while, but now I'm pretty confident with it and the resulting code benefits from it. I used to care way too much about spurious copies etc but lately I found that I hardly make classes copyable anymore, only movable, which also automatically seemed to make typical issues with sharing and lifetime fade away. (1) sometimes looking at assembly, which is always a good way to get deep understaning, mostly by using 'Noisy' classes like class Noisy { public: Noisy() { print( "ctor" ); } Noisy( Noisy&& ) { print( "move ctor" ); } //etc, you get the point }; and using it in cases like yours. And yes defining move vs copy constructor might change what gets invoked but if you have to care that much which one you should ask yourself if you need the copying.
- Guvante 12y agoShouldn't your move vs copy constructors be generally irrelevant? The idea is that they are semantically equivalent thus the compiler can make the correct choice for you. Your comment about RVO isn't accurate, those are two separate returns with rvalues (which can thus use a move constructor). I would heartily agree that tooling to allow inspection about how all of these rules apply would help tremendously.
- maxs 12y agoThese days I am writing a lot of C++11 code. I find that I write two different kinds of objects: 1. copyable objects like class RGB { int r, g, b }; 2. big complex things that should never copy but can move class Foo { Foo(const Foo&) = delete; }; In case of (1), I don't care if move or copy constructors are used and I am happy that the compiler will decide. In case of (2), if by accident I invoke a copy constructor, I end up getting a compile-time error.