Return Value Optimization and Destructor Calls

I know that RVO is mainly used, but can I count on it? I have a function that creates an object of the FlagContainer class.

class FlagContainer { public: ~FlagContainer() { someItem->flag = true; } private: Item * someItem; } public FlagContainer createFlagContainer() { return FlagContainer(); } 

After calling the container, the checkbox should be checked. Therefore, I can do this with a destructor.

 { FlagContainer container = createFlagContainer(); // do something with container } 

When leaving the scope, it will call the destructor. But can I be sure that the destructor will never be called in createFlagContainer? Is there any way to achieve this?

I would use the AVR GCC 4.7.0 compiler.

+5
source share
4 answers

I know that RVO is mainly used, but can I count on it?

Do not rely on RVO for logic. Simply put, compiling your program can be disabled using the command line.

Is there any way to achieve this?

Surprisingly, the standard library already gives you this functionality, so you do not need to risk implementing it yourself (moving constructors and operators, as you know, is difficult to obtain).

std::unique_ptr with user deletion does the job nicely.

 #include <iostream> #include <memory> #include <cassert> // test type struct X { bool flag = false; }; // a custom deleter that sets a flag on the target struct flag_setter_impl { template<class X> void operator()(X* px) const { if (px) { assert(!px->flag); std::cout << "setting flag!" << std::endl; px->flag = true; } } }; // a type of unique_ptr which does not delete, but sets a flag template<class X> using flag_setter = std::unique_ptr<X, flag_setter_impl>; // make a flag_stter for x template<class X> auto make_flag_setter(X& x) -> flag_setter<X> { return flag_setter<X>(&x, flag_setter_impl()); } // quick test auto main() -> int { using namespace std; X x; { auto fs1 = make_flag_setter(x); auto fs2 = move(fs1); } return 0; } 

but i don't have stl on my target

Then do not forget your rules 0, 3, 5

 #include <iostream> #include <memory> #include <cassert> // test type struct X { bool flag = false; }; // a custom deleter that sets a flag on the target struct flag_setter_impl { template<class X> void operator()(X* px) const { if (px) { assert(!px->flag); std::cout << "setting flag!" << std::endl; px->flag = true; } } }; // a type of unique_ptr which does not delete, but sets a flag template<class X> struct flag_setter { flag_setter(X* px) : px(px) {} flag_setter(const flag_setter&) = delete; flag_setter(flag_setter&& r) noexcept : px(r.px) { r.px = nullptr; } flag_setter& operator=(const flag_setter& r) = delete; flag_setter& operator=(flag_setter&& r) { flag_setter tmp(std::move(r)); std::swap(tmp.px, px); return *this; } ~flag_setter() noexcept { flag_setter_impl()(px); } private: X* px; }; // make a flag_stter for x template<class X> auto make_flag_setter(X& x) -> flag_setter<X> { return flag_setter<X>(&x); } // quick test auto main() -> int { using namespace std; X x; { auto fs1 = make_flag_setter(x); auto fs2 = move(fs1); } return 0; } 
+5
source

There is no guarantee that copying is applied. "Guaranteed copying" is proposed for inclusion in C ++ 17. Regardless of whether copy-copying is used, it completely depends on the compiler mastering (some compilers have the ability to completely disable it).

A potential approach that eliminates this need may be to use a substantially unsuitable type, which can only be used as an argument to the constructor for the type that you are interested in using, and to return an object of this type:

 class FlagContainerBuilder { friend class FlagContainer; public: FlagContainerBuilder(/* suitable arguments go here */); // nothing goes here }; class FlagContainer { // ... public: FlagContainer(FlagContainerBuilder&& builder); // as before }; FlagContainerBuilder createFlagContainer() { ... } 

This way you avoid having to potentially destroy the FlagContainer returned from createFlagContainer() .

+2
source

I know that RVO is mainly used, but can I count on it?

No. Compilers are allowed to implement RVO, but not required. You can only count on this when your promises compiler does this.

+1
source

Although this particular case in accordance with the standard 12.8 / 3 / p31.1 Copying and moving objects of the class [class.copy] displays as a context that the compiler can perform NRVO (otherwise a copy of elision), you cannot rely on it, the Program, which relies on such optimization is practically not portable.

To ensure the movement of an object, I would define a movement constructor, and inside I would reset the pointer to another object, while in the destructor I would check if the pointer is nullptr to set its flag to true:

 class FlagContainer { public: FlagContainer(FlagContainer&& other) : someItem(other.someItem) { other.someItem = nullptr; } ~FlagContainer() { if(someItem) someItem->flag = true; } Item * someItem; }; FlagContainer createFlagContainer() { return FlagContainer(); } 

Live demo

+1
source

Source: https://habr.com/ru/post/1237756/


All Articles