按概念上不应复制的值返回对象
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( ) };
}
}
我想要一个对象,其构造函数充当 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( ) };
}
}