返回值优化和析构函数调用

时间:2015-12-09 13:17:33

标签: c++ c++11

我知道RVO主要应用但我可以指望吗?我有一个函数创建一个FlagContainer类的对象。

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

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

调用者使用容器后,必须设置标志。所以我可以用析构函数来做这个。

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

当超出范围时,将调用析构函数。但是我可以确定在createFlagContainer中永远不会调用析构函数吗?有没有办法实现这个目标?

我会使用AVR GCC 4.7.0编译器。

4 个答案:

答案 0 :(得分:5)

  

我知道RVO主要应用但我可以指望吗?

不要依赖RVO来获取逻辑。简而言之,编译程序的人可以使用命令行选项将其关闭。

  

有没有办法实现这个目标?

令人惊讶的是,标准库已经为您提供了这一功能,因此您不需要自己承担实施它的风险(移动构造函数和运算符非常难以正确实现)

带有自定义删除工具的

std::unique_ptr可以很好地完成工作。

#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;
}
  

但我的目标不是STL

然后不要忘记你的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;
}

答案 1 :(得分:2)

无法保证[尚未]应用复制省略。建议将Guaranteed copy-elision纳入C ++ 17。是否应用copy-elision完全由编译器决定(虽然有些编译器可以选择完全禁用它)。

避免这种需求的潜在方法可能是使用一种本质上不可用的类型,它只能用作您感兴趣的类型的构造函数参数并返回该类型的对象:

var timer;
var sliderWrap = $('#sliderUL'),
    img = $('#sliderUL img')
    singleWidth = sliderWrap.find('img:first-child').width(),
    imageLenght = img.length,
    idCount = 1,
    current = 1,
    firstImage = img.first();
    lastImage = img.last(),
    secondLast = imageLenght-1;
    secondImage = img.eq(1);
    firstImage.addClass('active');

    img.each(function(){
        $(this).attr('id','slider_'+idCount++);
    });

    if(current === 1){
        $('#back').addClass('none');
    }

    $('#next').on('click',nextFun);
    $('#back').on('click',backFun);
    function backFun(){
clearInterval(timer);
setTimeout(function(){ timer = setInterval(autRotatae,2000);},3000);
        if(current === imageLenght){
            $('#next').removeClass('none');
        }
        if(current === 2){
            $(this).addClass('none');
        }

        var backNextSlider = sliderWrap.find('img[id="slider_'+(current-1)+'"]').addClass('active').removeClass('Subactive');
        sliderWrap.find('img[id="slider_'+(current)+'"]').removeClass('Subactive').addClass('backActive').removeClass('active');
        current--;
    }       
    function nextFun(){
clearInterval(timer);
setTimeout(function(){ timer = setInterval(autRotatae,2000);   },3000);
        var currentNextSlider = sliderWrap.find('img[id="slider_'+(current+1)+'"]').addClass('active').removeClass('backActive');
        sliderWrap.find('img[id="slider_'+(current)+'"]').addClass('Subactive').removeClass('backActive, active');
        current++;
        if(current === 2){
            $('#back').removeClass('none');
        }
        if(current === imageLenght){
            $('#next').addClass('none');
        }
    }       
        var mode = 'Next';
        function autRotatae(){
            if(current == imageLenght)
                mode = 'Back';
                if(current == 1)
                    mode = 'Next';      
                if(mode == 'Next')
                {
                    nextFun();
                }   
            else
                backFun();
        }
        $(document).ready(function(){
            timer = setInterval(autRotatae,2000);   
        })

这样您就可以避免破坏从class FlagContainerBuilder { friend class FlagContainer; public: FlagContainerBuilder(/* suitable arguments go here */); // nothing goes here }; class FlagContainer { // ... public: FlagContainer(FlagContainerBuilder&& builder); // as before }; FlagContainerBuilder createFlagContainer() { ... } 返回的FlagContainer

答案 2 :(得分:1)

  

我知道RVO主要应用但我可以指望吗?

没有。允许编译器实现RVO,但不是必需的。当你的编译器承诺这样做时,你只能指望它。

答案 3 :(得分:1)

虽然这个特殊情况按照标准12.8 / 3 / p31.1复制和移动类对象[class.copy]呈现为编译器可以执行NRVO(也就是复制省略)的上下文,但你不能依赖它。依赖于这种优化的程序实际上是不可移植的。

为了确保移动对象,我将定义一个移动构造函数,在内部我将使另一个对象的指针为空,而在析构函数中,我将检查指针是否为class FlagContainer { public: FlagContainer(FlagContainer&& other) : someItem(other.someItem) { other.someItem = nullptr; } ~FlagContainer() { if(someItem) someItem->flag = true; } Item * someItem; }; FlagContainer createFlagContainer() { return FlagContainer(); } ,以便将其标志设置为true :

{{1}}

Live Demo