我踩过的c++的坑 续

 

之前我写过一篇长期更新的《我踩过的c++的坑》,因为时间很长,文章太长了,查看起来太麻烦了,所以以后遇到的坑都记在这篇续里

旧文链接

持续更新

关于返回右值引用或使用移动语义可能会导致的问题

今天写了一个这样的函数

1
2
3
4
5
6
T&& f()
{
T t;
//...
return move(t);
}

这样的代码显然是错误的,因为右值引用实际上也是一种引用,而返回一个临时变量的引用是会出问题的。可以通过编译但是退出f时会报错(在vs2019上,理论上也有可能返回不正确的临时堆栈内容)。

以下的代码才是正确的演示,只要不反回临时变量的都行。

1
2
3
4
5
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{
return static_cast<S&&>(a);
}

可以看出来我的目的是为了避免一次拷贝。

但是这样的代码也是错的。

1
2
3
4
5
6
T f()
{
T t;
//...
return move(t);
}

因为

实际上这种是不需要的。因为编译器会做返回值优化(Return Value Optimization)。在C++11标准中有如下规定:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

直接return x;是NRVO支持的一种用例场景,可以做到多余的拷贝构造。编译器会自己选择使用拷贝构造还是move构造函数。

但是如果用std::move(x);那么可能会带来额外的影响:可能会阻止NRVO。也就是说可能需要额外的开销来执行move语义。

学艺不精,哎。一直没有研究过右值作为返回值,今天出问题了。以后学东西还是要实践同行。

函数形参中的左值引用不能置入匿名变量(不会自动折叠)

1
2
void fun(T& t);
func(T());

T()不会自动折叠T&

只能这样写

1
2
3
auto t = T()
void fun(T& t);
func(t);

头文件中不能出现定义

只能出现声明哦,不然一定会出现链接问题。

像是全局变量应该在头文件中

1
extern T t;

.cpp中

1
T t = t0;

匿名对象就是右值,不能传入左值引用中

像是T(arg...)这样的匿名对象本质上和1 1.0这样的数值常量差不多。如果你的函数参数是 const T&T&,的话是不能正常调用的。事实上不能通过编译。因为在调用函数后匿名对象就析构了,然后函数里传进去的引用就没有意义了。

正确的函数格式

1
2
3
void func(T t);

void func(T&& t);