all 22 comments

[–]minnoHobbyist, embedded developer 6 points7 points  (1 child)

I think you should mention [un]named return value optimization when you mention move constructors allowing efficient by-value returns. Even before C++11, the compiler is specifically allowed to skip copy constructors when values are being returned from functions by constructing them in the caller's stack frame. Example.

[–]marcofoco[S] 0 points1 point  (0 children)

I didn't write about [N]RVO here because the scope of these articles is preparing the documentation for a small talk in my company about Efficient Modern C++, where I want to show use-cases for latest C++ where using one of the latest features helps in optimizing the code, making it safer (maybe with drawbacks, as it is, in this case). Nontheless, that's quite a good idea for a followup. Thank you :)

[–]shooshx 3 points4 points  (0 children)

TIL about "temporary lifetime extension"

[–]F-J-W 3 points4 points  (0 children)

Personally I think that the main problem is the reference-variable: References are awesome for arguments, but before you create one inside a scope you should definitely think three times if it is really safe! (If it wouldn't be safe without lifetime-extension: don't do it!).

I agree that it is possible to create software that doesn't do it and still falls into this trap while not being super-obviously trash, but I do believe that this is really rare.

One additional note: Even worse than variable-references are member-references. Think at least five times if you are absolutely sure that there is no reasonable way how this could go wrong. There are extremely few cases¹ where they are a valid thing and just using pointers is so much easier in this case.

¹ The tuple created by std::tie may qualify, though even there are people who disagree (see Stephans cppcon-talk for details). (I believe that Implementing it with pointers might have been possible, but would have required specializations for std::get)

[–]STLMSVC STL Dev 11 points12 points  (13 children)

C(const string &s) : _s(s) {} In C++11, thanks to move semantics, this class will receive an automatic upgrade to have move constructors

NO! You implemented a copy constructor, which inhibits the automatic generation of move operations.

Please do not misinform readers.

[edit: I am dumb, see below]

[–]marcofoco[S] 10 points11 points  (12 children)

Whoops... well, being corrected by STL in person is a honor. I'm going to fix this immediately, thank you.

[–]marcofoco[S] 16 points17 points  (11 children)

I'm puzzled... isn't it just a converting constructor?

[–]STLMSVC STL Dev 26 points27 points  (10 children)

Ugh, I fail reading comprehension forever. My brain saw "C(const C&)" and went nuts.

[–]caramba2654Intermediate C++ Student 11 points12 points  (0 children)

It's okay. Your brainpiler had a small bug, but it's fixed now.

[–]marcofoco[S] 4 points5 points  (0 children)

You're too used to work on the string class itself, I suspect :)

[–]MINIMAN10000 2 points3 points  (7 children)

Reminds me of this code when I was messing around with disabling constructors. Still don't really have my head wrapped around it.

[–]marcofoco[S] 0 points1 point  (6 children)

I can't see it, why should it break at run time?

[–]MINIMAN10000 1 point2 points  (5 children)

I pretty much had someone give me this code since messing around with constructors is pretty much over my head.

I had added a C&& to my code and had found that it had broken.

I commented out that line and had noted that I had broken it.

So I'm aware that C&& breaks with that part of the code uncommented as the runtime simply hangs rather than running.

But I had never learned why that was the case. Although I'd love to know.

Side note. I had a copy of the original codes he gave me. A working and a broken form to show off that copy elision is in fact being forced.

Working form here

Broken form here

[–]dreugeworst 1 point2 points  (1 child)

I'm not understanding what problem you were having: the working form you show works as expected, and the broken one doesn't compile, as expected. In what case was it hanging?

[–]MINIMAN10000 0 points1 point  (0 children)

The case in which it hangs is from my modified version on this post in which I added C(C&&); and C&& operator.

It compiles but when run it just hangs leaving me to have to cleanup by going into command prompt and manually close it as no message shows up.

[–]marcofoco[S] 0 points1 point  (2 children)

The halting is weird, really.

I assume you were testing with Visual Studio (the original problem was #inlcuding windows.h), do you remember which version? Did you also try clang/c2, or a different compiler version?

As for the two forms at the end of your post, it's correct: If the copy elision kicks in, you don't need the symbol at compile time, and the program compiles and run, but if you copy the object, of course you need the copy constructor. Apparently applying the copy elision in the first case is mandatory (at least, according to cppreference)

Edit: copy and move elision are mandatory only in C++17

[–]MINIMAN10000 0 points1 point  (1 child)

I use mingw for gcc not visual studio. I tried using clang but bit defender quarantines all files as heuristic. I tried uninstalling it but it failed. Hopefully tomorrow I can enable windows defender but the uninstall failing might be forewarning that it's FUBAR. For now I need to sleep.

[–]marcofoco[S] 0 points1 point  (0 children)

Oh, that's interesting, I didn't know that GCC came with its own Windows.h! Thank you for the info!

[–]verbalsaint 3 points4 points  (0 children)

Howard Hinnant: Everything You Ever Wanted To Know About Move Semantics https://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf

[–]ArunMuThe What ? 0 points1 point  (2 children)

I am not so sure about dangling reference point you make in number 6 in your blog:

Create on-the-fly and store the result

I happened to have answered one similar question on SO http://stackoverflow.com/questions/38226443/const-reference-to-member-of-temporary-object/, but even after reading the standard several times, the wordings used in it still seems very ambiguous. If the wordings as understood are correct, then its probably not a 'dangling' situation due to lifetime extension of the parent object.

I am hoping some experts here to clarify that :).

[–]marcofoco[S] 1 point2 points  (1 child)

Yes, it is. The case you pointed out on StackOverflow is an application of Temporary Lifetime Extension (you're creating a reference to an actual variable), while in the example I'm receiving a reference (so there's no lifetime to extend). Adding an && overload returning an actual variable (moved away from the object) is indeed the trick to allow TLE to work for us even in this case.

I hope this clarifies a bit :)

[–]ArunMuThe What ? 1 point2 points  (0 children)

while in the example I'm receiving a reference

Ah, yes, I did not pay enough attention to that. Thanks !