Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return value optimization and destructor calls

Tags:

c++

c++11

I know that RVO is mostly applied but can I count on it? I have a function that creates an object of class FlagContainer.

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

public FlagContainer createFlagContainer() {
    return FlagContainer();
}

After the caller used the container, the flag must be set. So I can do this with the destructor.

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

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

I would use AVR GCC 4.7.0 compiler.

like image 590
DreyFax Avatar asked Dec 09 '15 13:12

DreyFax


People also ask

Can destructors return a value?

Do not return a value (or void ). Cannot be declared as const , volatile , or static . However, they can be invoked for the destruction of objects declared as const , volatile , or static .

Is destructor called after return?

While returning from a function, destructor is the last method to be executed. The destructor for the object “ob” is called after the value of i is copied to the return value of the function. So, before destructor could change the value of i to 10, the current value of i gets copied & hence the output is i = 3.

Is return value optimization guaranteed?

Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.

What is return object optimization?

In the context of the C++ programming language, return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function's return value. RVO is allowed to change the observable behaviour of the resulting program by the C++ standard.


1 Answers

I know that RVO is mostly applied but can I count on it?

Don't rely on RVO for logic. Put simply, someone compiling your program can switch it off with a command-line option.

Is there any way to achieve this?

Surprisingly, the standard library already gives you this functionality so you don't need to run the risk of implementing it yourself (move constructors and operators are notoriously difficult to get right)

std::unique_ptr with a custom deleter 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 the STL on my target

Then don't forget your rules of 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;
}
like image 173
Richard Hodges Avatar answered Oct 05 '22 08:10

Richard Hodges