近日结合李伟老师在C++构造函数课上讲的内容,跑了一些程序,发现了一些疑问。
程序如下
#include <iostream>
using namespace std;
class str
{
public:
str(int x):a(x)
{
cout<<"construct function"<<endl;
}
str(const str& str1):a(str1.a)
{
cout<<"copy construct function"<<endl;
}
str(str&& str1):a(move(str1.a))
{
cout<<"move construct function"<<endl;
}
str& operator =(const str& val)
{
a=val.a;
cout<<"copy operator used"<<endl;
return *this;
}
private:
int a;
};
int main()
{
str a{str(2)};
}
本来认为结果应该是先进行一次直接构造函数,在进行一次移动构造函数,但实际输出竟然是这样。
可以看出,结果显示只进行了一次直接构造,这与预想的结果不太相同。查阅了相关资料后发现,编译器对C++默认有一个优化。手册上的解释是这样的。
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary that is only used to initialize another object of the same type. Specifying this option disables that optimization, and forces G++ to call the copy constructor in all cases.
大意为:C++标准允许一种(编译器)实现省略创建一个只是为了初始化另一个同类型对象的临时对象。指定这个参数(-fno-elide-constructors)将关闭这种优化,强制G++在所有情况下调用拷贝构造函数。
编译时添加这个参数,运行结果如下
可以看出来结果符合我们的预期。
李伟老师在课程中提到最好不要对移动构造函数引入delete限定符,是因为C++17有一个优化,这句话听课的时候不太明白,先不加限定符在C++17下跑一下试试。
发现竟然又只输出了一条,但是我们明明加了-fno-elide-constructors,我认为老师所说的优化应该指的就是这个吧,不管加不加-fno-elide-constructors,编译器都会进行优化。
对这个程序移动构造函数添加了delete后,先在C++11标准下编译一下试试。
可以看出来,编译出现了错误,这就很迷了,之前明明分析的是编译器会默认对其构造函数引入优化,不应该没使用移动构造函数吗?
再在C++17下跑一下。
编译并没有出错。
于是我认为,整个的过程应该是,编译器默认会对构造函数引入优化,但C++11标准下这个优化并不彻底,不能对移动构造函数加delete(虽然没有用到移动构造函数),且这个优化可以通过添加-fno-elide-constructors来关闭。而C++17后这个优化就很彻底,构造函数加不加delete已经无所谓了(反正也没用到),且无法通过加-fno-elide-constructors来关闭。而之所以不建议对移动构造函数加delete的原因之一可能就是怕这个不彻底的优化被发现吧?(个人猜测)
只是个人理解,如有问题请指出!