Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么不是';std::move返回的t对象立即销毁_C++_C++11 - Fatal编程技术网

C++ 为什么不是';std::move返回的t对象立即销毁

C++ 为什么不是';std::move返回的t对象立即销毁,c++,c++11,C++,C++11,我测试了以下代码: #include <iostream> using namespace std; class foo{ public: foo() {cout<<"foo()"<<endl;} ~foo() {cout<<"~foo()"<<endl;} }; int main() { foo f; move(f); cout<<"statement \"m

我测试了以下代码:

#include <iostream>
using namespace std;
class foo{
public:
    foo()       {cout<<"foo()"<<endl;}
    ~foo()      {cout<<"~foo()"<<endl;}
};

int main()
{
    foo f;
    move(f);
    cout<<"statement \"move(f);\" done."<<endl;
    return 0;
}
然而,我预计

foo()
~foo()
statement "move(f);" done.
根据函数move的源代码:

  template<typename _Tp>
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

因为在一般情况下,移动可能发生在另一个翻译单位。在您的示例中,对象甚至没有移动,只是标记为可移动。这意味着
std::move
的调用方将不知道对象是否被移动,他只知道存在一个对象,并且它必须在该对象的作用域/生存期结束时调用析构函数
std::move
仅将对象标记为可移动,它不执行移动操作或创建可进一步移动的移动副本或类似操作

考虑:

// translation unit 1
void f( std::vector< int >&& v )
{
  if( v.size() > 8 ) {
    // move it
  }
  else {
    // copy it as it's just a few entries
  }
}

// translation unit 2

void f( std::vector< int >&& );
std::vector< int > g();

int main()
{
  // v is created here
  std::vector< int > v = g();

  // it is maybe moved here
  f( std::move( v ) );

  // here, v still exists as an object
  // when main() ends, it will be destroyed
}
//翻译单元1
空f(标准::向量&&v)
{
如果(v.size()>8){
//移动它
}
否则{
//复制它,因为它只是几个条目
}
}
//翻译单元2
void f(标准向量&);
std::vectorg();
int main()
{
//v是在这里创建的
std::vectorv=g();
//它可能搬到这里来了
f(std::move(v));
//在这里,v仍然作为一个对象存在
//main()结束时,它将被销毁
}

在上面的示例中,翻译单元2如何决定在
std::move
之后是否调用析构函数?

从对象移动不会改变其生存期,只会改变其当前值。您的对象
foo
在从
main
返回时被销毁,该返回位于您的输出之后


此外,
std::move
不会从对象移动。它只返回一个右值引用,它的refereand是对象,这样就可以从对象中移动。

对象在超出范围时会被销毁。从一个物体上移动并不会改变这一点;根据移动构造函数或移动赋值操作符所做的操作,对象在移动后的状态可能不同,但尚未被销毁,因此实际规则是从对象移动时必须将其保持在可以销毁的状态

除此之外,正如@R.MartinhoFernandes指出的,
std::move
没有任何作用。它是对象的move构造函数或move赋值运算符,它执行需要执行的任何操作,并且不应用于调用
std::move
;当“移动自”对象用于构造新对象(移动构造函数)或指定给现有对象(移动指定操作符)时,将应用此选项。像这样:

foo f;
foo f1(f);            // applies foo's copy constructor
foo f2(std::move(f)); // applies foo's move constructor
foo f3, f4;
f3 = f;               // applies foo's copy assignment operator
f4 = std::move(f1);   // applies foo's move assignment operator

假设代码如下:

void bar(Foo& a) {
  if (a.noLongerneeded())
    lastUnneeded = std::move(a);
}
在这种情况下,
bar
的调用方无法知道函数在某些情况下可能最终调用传递对象的析构函数。它将感觉对该对象负责,并确保在以后的任何时候调用其析构函数


因此,规则是,
move
可以将有效对象转换为不同但仍然有效的对象。仍然有效意味着在该对象上调用方法或析构函数仍应产生定义良好的结果。严格地说,
move
本身不做任何事情,只是告诉这样一个引用的接收者,如果这样做有意义,它可能会改变对象。因此,是接收者,例如移动构造函数或移动赋值运算符,执行实际移动。这些操作通常不会更改对象,或者将某些指针设置为
nullptr
,或者将某些长度设置为零或类似的值。但是,它们永远不会调用析构函数,因为该任务留给对象的所有者。

您会被名称搞糊涂--
std::move
实际上不会移动任何东西。它只是将左值引用转换(强制转换)为右值引用,并用于让其他人移动某物

其中,
std::move
非常有用,因为您有一个重载函数,该函数采用左值或右值引用。如果使用简单变量调用这样的函数,重载解析意味着您将使用左值引用调用版本。您可以添加对
std::move
的显式调用,以代替调用右值引用版本。不涉及任何移动,除非在接受右值引用的函数内

现在之所以称为move,是因为有两个构造函数,一个接受左值引用(通常称为copy构造函数),另一个接受右值引用(通常称为move构造函数)。在这种情况下,向std::move添加显式调用意味着调用move构造函数而不是copy构造函数


在更一般的情况下,通常的做法是让重载函数接受左值/右值引用,左值版本复制对象,右值版本移动对象(隐式修改源对象以接管其使用的任何内存)。

a
std::move
不会改变对象的生存期。粗略地说,它只不过是一个将非常量左值转换为非常量右值引用的
static\u cast

这样做的好处是解决过载问题。实际上,有些函数通过常量左值引用(例如复制构造函数)获取参数,而另一些函数通过非常量右值引用(例如移动构造函数)获取参数。如果传递的对象是临时对象,则编译器调用第二个重载。这个想法是,在函数被调用之后,临时函数就不能再使用(并且将被销毁)。因此,第二个过载可能会占用临时人员的资源,而不是应对它们

但是,编译器不会对非临时对象执行此操作(或者更正确地说,对于左值)。原因是传递的对象的名称保留在范围内,因此仍然可以使用它(如代码所示)。因此,它的内部资源可能仍然是必需的,如果它们已经移动到另一个对象,这将是一个问题。无极
// translation unit 1
void f( std::vector< int >&& v )
{
  if( v.size() > 8 ) {
    // move it
  }
  else {
    // copy it as it's just a few entries
  }
}

// translation unit 2

void f( std::vector< int >&& );
std::vector< int > g();

int main()
{
  // v is created here
  std::vector< int > v = g();

  // it is maybe moved here
  f( std::move( v ) );

  // here, v still exists as an object
  // when main() ends, it will be destroyed
}
foo f;
foo f1(f);            // applies foo's copy constructor
foo f2(std::move(f)); // applies foo's move constructor
foo f3, f4;
f3 = f;               // applies foo's copy assignment operator
f4 = std::move(f1);   // applies foo's move assignment operator
void bar(Foo& a) {
  if (a.noLongerneeded())
    lastUnneeded = std::move(a);
}
#include <iostream>

using namespace std;

class foo{
public:
    foo()  { cout << "foo()"  << endl; }
    ~foo() { cout << "~foo()" << endl; }
};

void g(const foo&) { cout << "lref" << endl; }
void g(foo&&)      { cout << "rref" << endl; }

int main()
{
    foo f;
    g(f);

    g(move(f));

    // f is still in scope and can be referenced.
    // For instance, we can call g(f) again.
    // Imagine what would happen if f had been destroyed as the question's author
    // originally though?

    g(static_cast<foo&&>(f)); // This is equivalent to the previous line

    cout<<"statement \"move(f);\" done."<<endl;
    return 0;
}
foo()
lref
rref
rref
statement "move(f);" done.
~foo()
foo(const foo& orig) { cout << "copy foo from " << &orig << " to " << this << endl;}      
foo() at 0xa74203de
copy foo from 0xa74203de to 0xa74203df
~foo() at 0xa74203df
statement "move(f);" done.
~foo() at 0xa74203de
constexpr typename /**/std::remove_reference<_Tp>::type /* no && */`