返回右值?或者:为什么复制构造函数在"返回"时被调用<expression>?
returning rvalue? Or : Why does copy constructor get called on `return <expression>`?
我怀疑我模糊地知道我所观察到的原因,但我希望得到确认或纠正和一些解释。
我有以下代码:
template <class T>
class C
{
public:
C () = default;
C(const C& rhs) : mem(rhs.mem)
{
std::cerr << "copy" << "n";
}
// does not call copy constructor twice
// friend C operator<<(C& src, unsigned shift)
// {
// std::cerr << 1 << "n";
// C tmp(src);
// std::cerr << 2 << "n";
// tmp <<= shift;
// std::cerr << 5 << "n";
// return tmp;
// }
// does call copy constructor twice
friend C operator<<(C& src, unsigned shift)
{
std::cerr << 1 << "n";
C tmp(src);
std::cerr << 2 << "n";
return (tmp <<= shift);
}
friend C& operator<<=(C& src, unsigned shift)
{
std::cerr << 3 << "n";
src.mem <<= shift;
std::cerr << 4 << "n";
return src;
}
T mem;
};
int main()
{
C<int> c1;
c1 << 3;
}
我有两个版本的C<T>::operator<<(C<T>, unsigned)
不同之处在于返回表达式的结果:
return (tmp <<= shift);
另一个返回一个变量:
return tmp
return (tmp <<= shift);
的函数只是更好的样式,如return a + 1
将比int ret = a + 1; return ret
更好的样式。显然并非如此,而且可能只适用于原子数据类型。return (tmp <<= shift);
版本的输出如下所示:
1
copy
2
3
4
copy
另一个的输出如下:
1
copy
2
3
4
5
我的假设是正确的,事实上return (tmp <<= shift);
在调用<<=
后不返回tmp
,而是在调用<<=
后创建一个新对象作为tmp
的副本?
按值返回时,必须初始化返回值。返回值是一个类型为C
的对象。
在return (tmp <<= shift);
的情况下,这意味着(tmp << shift)
是C
的初始化式。因为它是C
类型的左值,所以这是一个复制构造。
有一个特殊的规则,对于形式为return identifier;
的返回语句,即使identifier
是左值,它也可以是移动构造。但是这个规则还不能扩展到其他表达式。
另一个版本的代码会激活特殊规则:
tmp <<= shift;
return tmp;
这里tmp
可以被视为右值,使其可移动(这也使其成为一个复制省略上下文)。
在测试中,编译器实现了复制-省略。要在没有复制省略的情况下进行测试(如果您的编译器支持让用户配置),您还需要为C
提供一个move构造函数。用户提供的复制构造函数抑制了移动构造函数的隐式生成。
operator <<=
可能不会返回对输入参数的引用(可能会返回对另一个static
/global
变量的引用),因此编译器不能轻易决定使用返回值优化,而需要调用复制构造函数
在您的问题中:当您编写return (tmp <<= shift)
时,编译器不知道(tmp <<= shift)
是否会返回对tmp的引用,但如果您编写return tmp
,编译器知道它并可以优化它。
- 从python中调用C++函数并获取返回值
- 返回递归调用和仅递归调用的区别
- 获取从C++中同一类中的构造函数调用的方法返回的值
- 调用CreateProcess()并获取字符串的返回值
- 使用JsonCpp将数据返回到带有pybind11的python会在python调用中产生Symbol not foun
- 将错误返回给调用方而不是立即在 C++ 中抛出错误是否是一种好的做法
- 返回指向对象的指针的函数调用是否为 prvalue?
- 如何安全地从 DLL 调用返回对象
- getopt_long_only第二次调用时返回 -1
- 如何构造可以调用和返回两种不同类型的模板
- 从返回 std::optional of std::vector 的函数中获取结果到调用方
- C++如何使函数返回调用它的对象
- 谷歌嘲笑!我的测试出错.默认情况下,呼叫将返回调用
- 仅使用有关类型而不是对象的信息返回调用的类型
- 从C#返回调用C++dll函数的值时发生访问冲突
- 问题5.3:访问/返回/调用动态创建的复选框(?)
- 按值返回调用复制构造函数或复制赋值运算符
- Tcl_Eval在出错时返回调用堆栈
- 按值返回调用复制构造函数
- 为什么在没有显式返回语句的情况下,递归返回调用会跳出堆栈