按概念上不应复制的值返回对象

Returning an object by value which should conceptually not be copied

我想要一个对象,其构造函数充当 begin(),其析构函数充当 end(),并提供仅在这两个调用之间有效的函数作为方法。但是...我也想使用命名构造函数,并且也有充当此对象工厂的函数。

// this is the object I'm returning by value
class DrawCommand {
    DrawCommand(CanvasBuffer & guts, uint32 threadID); // private
public:
    // begin(); starts  a draw command
    static DrawCommand inMT(Canvas & canvas);     // for main thread
    static DrawCommand inWK(const Job & jobRef);  // for worker threads

    // end(); submits command to rendering thread
    ~DrawCommand(); 

    // returns false if draw can be ignored (doesn't need to be respected)
    operator bool();

    // commands which would break if used outside of begin() and end()
    void doStuff();
};

// this class has a factory that returns by value
class Canvas{
public:
    DrawCommand debugDrawCommandMT(){
        DrawCommand cmd;
        ...
        return cmd;
    }
};

// usage
{
    DrawCommand cmd1 = DrawCommand::inMT(canvas);
    cmd1.doStuff();
} // cmd1.~DrawCommand() upon exiting scope

if (DrawCommand cmd2 = canvas.debugDrawCommandMT()) {
    cmd2.doStuff();
} // cmd2.~DrawCommand() upon exiting scope

这意味着按值返回此对象。

这很麻烦,因为 RVO 是一个带有副作用的可选优化。省略时,这会提示调用构造函数和析构函数。这些函数很昂贵,因为它们访问由互斥体保护的资源,所以我需要避免这种行为。

什么是最简单的方法来确保此对象的行为就像返回时始终发生 RVO 一样?

具体来说,我所追求的是 RVO 的 行为 ,我已经能够找到有关它是什么以及如何暗示它的用法的信息编译器。但我想以可靠的方式获得 RVO 的副作用。从概念上讲,返回的对象应该不是副本,而是原始对象,即使那不是现实。

这就是我的意思,但是在代码中: (如果需要更改为 shared_ptr 并忘记移动构造函数而只使用复制构造函数)

#include <iostream>
#include <memory>

class ClassItf
{
public:
    virtual ~ClassItf() = default;

    virtual void DoWork() = 0;

protected:
    ClassItf(const ClassItf&) = delete;
    ClassItf(ClassItf&&) = delete;
    ClassItf& operator=(ClassItf&) = delete;
    ClassItf() = default;
};

// hide implementation 
namespace details
{
class ImplementationClass :
    public ClassItf
{
public:
    ImplementationClass()
    {
        std::cout << "ImplementationClass::ImplementationClass" << std::endl;
    }

    ~ImplementationClass()
    {
        std::cout << "ImplementationClass::~ImplementationClass" << std::endl;
    }

    virtual void DoWork() override
    {
        std::cout << "ImplementationClass::DoWork" << std::endl;
    }
};
}

class YourClass :
    public ClassItf
{
public:
    YourClass() :
        m_impl{ std::make_unique<details::ImplementationClass>() }
    {
    }

    YourClass(YourClass&& other) :
        m_impl{ std::move(other.m_impl) }
    {
        std::cout << "YourClass::YourClass moved" << std::endl;
    }

    virtual void DoWork() override
    {
        m_impl->DoWork();
    }

private:
    std::unique_ptr<ClassItf> m_impl;
};

YourClass CreateClass()
{
    YourClass retval;
    return retval;
}

int main()
{
    {
        auto obj = CreateClass();
        obj.DoWork();
    }
    std::cout << "Done..." << std::endl;
}

这就是我的想法。 您可以 运行 并查看控制台输出。

它是一种只能移动的类型(如果您尝试复制,您会看到有关已删除函数的编译器错误)。

如果对象已被移走,它会阻止将命令发送到线程。

class DrawCommand { 
public:
    explicit DrawCommand( std::string state ) noexcept
        : state_{ std::move( state ) }
        , moved_{ false }
    { }

    DrawCommand( const DrawCommand& ) = delete;
    DrawCommand& operator=( const DrawCommand& ) = delete;

    DrawCommand( DrawCommand&& other ) noexcept 
        : state_{ std::exchange( other.state_, { } ) }
        , moved_{ std::exchange( other.moved_, true ) }
    { } 

    DrawCommand& operator=( DrawCommand&& other ) noexcept { 
        state_ = std::exchange( other.state_, { } );
        moved_ = std::exchange( other.moved_, true );
        return *this;
    }

    ~DrawCommand( ) {
        if ( moved_ ) std::cout << "Skip sending to thread\n";
        else std::cout << "Sending " << state_ << '\n';
     }
private:
    std::string state_;
    bool moved_;
};

static auto create_command( ) -> DrawCommand { 
    return DrawCommand{ "Some state" };
}

auto main( ) -> int { 
    { 
        auto cmd{ create_command( ) };
    }
}