C++ 如何编写具有仅移动属性的函数?

C++ 如何编写具有仅移动属性的函数?,c++,c++11,move,rvalue-reference,C++,C++11,Move,Rvalue Reference,我有一个类型是移动只,复制是禁止的。 我想在某些系统中传递它,但我不确定对于采用该类型参数的函数使用哪种签名。这种类型的对象必须移动到系统中,不应进行复制 例如: #include <vector> class Foo { public: Foo( std::string name ) : m_name( std::move( name ) ){} Foo( Foo&& other ) : m_name( std::move( other.m_name

我有一个类型是移动只,复制是禁止的。 我想在某些系统中传递它,但我不确定对于采用该类型参数的函数使用哪种签名。这种类型的对象必须移动到系统中,不应进行复制

例如:

#include <vector>
class Foo
{
public:
    Foo( std::string name ) : m_name( std::move( name ) ){}
    Foo( Foo&& other ) : m_name( std::move( other.m_name ) ){}
    Foo& operator=( Foo&& other ){ m_name = std::move( other.m_name ); }

    const std::string& name() const { return m_name; }
    // ...
private:
    Foo( const Foo& ) ;//= delete;
    Foo& operator=( const Foo& ) ;//= delete;
    // ...

    std::string m_name;
};

class Bar
{
public:

    void add( Foo foo )  // (1)
        // or...
    void add( Foo&& foo ) // (2)
    {
        m_foos.emplace_back( std::move(foo) ); // if add was template I should use std::forward?
    }


private:
    std::vector<Foo> m_foos;
};

void test()
{
    Bar bar;
    bar.add( Foo("hello") );

    Foo foo("world");
    bar.add( std::move(foo) );
}
#包括
福班
{
公众:
Foo(std::string name):m_name(std::move(name)){}
Foo(Foo&&other):m_名称(std::move(other.m_名称)){}
Foo&operator=(Foo&other){m_name=std::move(other.m_name);}
常量std::string&name()常量{return m_name;}
// ...
私人:
Foo(const Foo&);//=delete;
Foo&运算符=(const Foo&);//=delete;
// ...
std::字符串m_名称;
};
分类栏
{
公众:
无效添加(Foo Foo)/(1)
//或者。。。
无效添加(Foo和&Foo)/(2)
{
m_foos.emplace_back(std::move(foo));//如果add是模板,我应该使用std::forward吗?
}
私人:
std::向量m_foos;
};
无效测试()
{
酒吧;
加上(Foo(“你好”);
福福(“世界”);
添加(标准::移动(foo));
}
(假设我正在移动对象,两个签名都在VS2012中编译)

1和2之间的哪个签名应该是首选

看起来这两种方法都有效,但我认为一定有不同之处…

这要看情况而定

如果要将对象移动到函数中,请按值获取它。编译器将自己进行移动,您将清楚地向调用方记录函数移动传递的参数。如果使用第二个参数,函数可能会移动参数,也可能不会移动参数,即使调用者必须使用
std::move(foo)
。这可能会让人非常困惑

如果不想将对象移动到函数中,请使用左值引用。如果函数未发生变异,则将其设为常量;如果函数发生变异,则将其设为非常量。

视情况而定

如果要将对象移动到函数中,请按值获取它。编译器将自己进行移动,您将清楚地向调用方记录函数移动传递的参数。如果使用第二个参数,函数可能会移动参数,也可能不会移动参数,即使调用者必须使用
std::move(foo)
。这可能会让人非常困惑

如果不想将对象移动到函数中,请使用左值引用。如果函数未发生变异,则将其设置为常量;如果函数发生变异,则将其设置为非常量。

从xvalue移动时,签名#1将花费2个移动构造,而签名#2只需要从xvalue移动1个移动构造。因此#2对我来说更合适。但只要搬家建筑成本低廉,任何一个签名都能完成这项工作。您想要:

  • 真的很快,或者
  • 比真的快两倍
  • :-)

    签名#1在从xvalue移动时需要2个移动构造,而签名#2只需要从xvalue移动1个移动构造。因此#2对我来说更合适。但只要搬家建筑成本低廉,任何一个签名都能完成这项工作。您想要:

  • 真的很快,或者
  • 比真的快两倍

  • :-)

    Signature one无法编译,因为您无法复制
    Foo
    s。您应该使用(2)。@SethCarnegie是的,它会编译。@SethCarnegie如果他使用右值引用调用它会怎么样?就像在他的最后一行示例中一样。@SethCarnegie它在VS2012中工作,当你移动对象时。签名一不会编译,因为你不能复制
    Foo
    s。您应该使用(2)。@SethCarnegie是的,它会编译。@SethCarnegie如果他使用右值引用调用它会怎么样?就像在他的最后一行示例中一样。@SethCarnegie它在VS2012中工作,当你移动对象时。我真的想移动对象。对象类型的语义确实需要它,因为复制是无意义的(如std::thread或类似类型)。我会补充一些精确性。@Klaim那么签名#1就是正确的选择。你能补充解释一下为什么在2)函数可能移动参数,也可能不移动参数吗?我对此有点困惑。@Klaim
    void f(foo&&){}
    不会移动参数。然而,
    void f(foo){}
    。我的意思是,第一个签名强制移动,但第二个签名给实现留出了余地。我真的想移动对象。对象类型的语义确实需要它,因为复制是无意义的(如std::thread或类似类型)。我会补充一些精确性。@Klaim那么签名#1就是正确的选择。你能补充解释一下为什么在2)函数可能移动参数,也可能不移动参数吗?我对此有点困惑。@Klaim
    void f(foo&&){}
    不会移动参数。然而,
    void f(foo){}
    。我的意思是,第一个签名强制移动,但第二个签名为实现提供了余地。