为什么临时'Test'没有动,只是构造?
Why did not the temporary 'Test' did not moved, but only constructed?
我正在阅读 Scott Meyers 的 Effective Modern C++ 这本书,作者在其中提到
"Copies of rvalues
are generally move constructed, while copies of
lvalues
are usually copy constructed."
所以我想通过编写一个 Test
class 来测试它,它将临时构建到一个 dummy
函数,该函数将右值引用作为 [= 的参数16=] class.
我期待看到上面引用的输出,如:
Constructed...
Moving...
Hello world
但我只有:
Constructed...
Hello world
为什么移动构造函数没有调用?像 dummy(Test{});
这样临时传递给 dummy
是否有任何未定义的行为?我正在使用 C++17.
代码如下:
#include <iostream>
class Test
{
public:
Test() { std::cout << "Constructed...\n"; }
Test(const Test&) { std::cout << "Coping...\n"; }
Test& operator=(const Test&) { std::cout << "Copy=...\n"; return *this; }
Test(Test&&) { std::cout << "Moving...\n"; }
Test& operator=(Test&&) { std::cout << "Move=...\n"; return *this; }
void print() const
{
std::cout << "Hello world\n";
}
};
void dummy(Test&& test)
{
test.print(); // is the behavior is good?
}
int main()
{
dummy(Test{});
return 0;
}
基本上,只构造了一个 Test 实例:dummy(Test{})
但是如果要从一个实例移动到另一个实例,则此移动意味着至少存在两个实例。
出于同样的原因,以下代码不会调用复制构造函数:
class Test {
...
};
void dummy(Test& t) { // passed as an lvalue reference
test.print();
}
int main() {
Test t;
dummy(t);
return 0;
}
...因为对象是通过引用传递的。在上面的代码中,它是一个左值引用,而在你给出的例子中它是一个右值引用,但仍然是一个引用。
如果你想按值传递相反,你需要在你的函数参数类型中反映它:
void dummy(Test t) {
...
}
然后需要调用构造函数以按值传递t
除非发生复制省略,请参见下面的添加。
请注意 std::move
实际上并没有移动任何东西。它将输入转换为右值引用并 returns 它。
虽然有一点细微差别。 C++17 保证不会为纯右值赋值创建临时对象,即 dummy(Test{})
在这种情况下不会调用复制或移动构造函数,因为创建临时对象被省略了。尽管语言不需要,但复制省略可能仍会在 C++17 之前的版本中发生。因此,如果您想观察在将 prvalue Test{}
按值传递给 dummy
函数时调用移动构造函数(如上例所示),请确保设置 -fno-elide-constructors
编译器标志。
我正在阅读 Scott Meyers 的 Effective Modern C++ 这本书,作者在其中提到
"Copies of
rvalues
are generally move constructed, while copies oflvalues
are usually copy constructed."
所以我想通过编写一个 Test
class 来测试它,它将临时构建到一个 dummy
函数,该函数将右值引用作为 [= 的参数16=] class.
我期待看到上面引用的输出,如:
Constructed...
Moving...
Hello world
但我只有:
Constructed...
Hello world
为什么移动构造函数没有调用?像 dummy(Test{});
这样临时传递给 dummy
是否有任何未定义的行为?我正在使用 C++17.
代码如下:
#include <iostream>
class Test
{
public:
Test() { std::cout << "Constructed...\n"; }
Test(const Test&) { std::cout << "Coping...\n"; }
Test& operator=(const Test&) { std::cout << "Copy=...\n"; return *this; }
Test(Test&&) { std::cout << "Moving...\n"; }
Test& operator=(Test&&) { std::cout << "Move=...\n"; return *this; }
void print() const
{
std::cout << "Hello world\n";
}
};
void dummy(Test&& test)
{
test.print(); // is the behavior is good?
}
int main()
{
dummy(Test{});
return 0;
}
基本上,只构造了一个 Test 实例:dummy(Test{})
但是如果要从一个实例移动到另一个实例,则此移动意味着至少存在两个实例。
出于同样的原因,以下代码不会调用复制构造函数:
class Test {
...
};
void dummy(Test& t) { // passed as an lvalue reference
test.print();
}
int main() {
Test t;
dummy(t);
return 0;
}
...因为对象是通过引用传递的。在上面的代码中,它是一个左值引用,而在你给出的例子中它是一个右值引用,但仍然是一个引用。
如果你想按值传递相反,你需要在你的函数参数类型中反映它:
void dummy(Test t) {
...
}
然后需要调用构造函数以按值传递t
除非发生复制省略,请参见下面的添加。
请注意 std::move
实际上并没有移动任何东西。它将输入转换为右值引用并 returns 它。
虽然有一点细微差别。 C++17 保证不会为纯右值赋值创建临时对象,即 dummy(Test{})
在这种情况下不会调用复制或移动构造函数,因为创建临时对象被省略了。尽管语言不需要,但复制省略可能仍会在 C++17 之前的版本中发生。因此,如果您想观察在将 prvalue Test{}
按值传递给 dummy
函数时调用移动构造函数(如上例所示),请确保设置 -fno-elide-constructors
编译器标志。