在我工作的地方,人们通常认为对象最好使用C++样式构造(带括号)初始化,而原始类型应该用=运算符初始化:
std::string strFoo( "Foo" ); int nBar = 5;
但是,似乎没有人能够用这种方式解释他们为什么喜欢这样的东西.我可以看到std::string = "Foo";
效率低下,因为它会涉及额外的副本,但是=
完全放弃运算符并在各处使用括号有什么不对?
这是一个共同的惯例吗?这背后的想法是什么?
使用=运算符或使用构造函数调用初始化变量在语义上是相同的,这只是样式的问题.我更喜欢=运算符,因为它更自然地读取.
使用=运算符通常不会生成额外的副本 - 它只调用普通的构造函数.但请注意,对于非基本类型,这仅适用于与声明同时发生的初始化.相比:
std::string strFooA("Foo"); // Calls std::string(const char*) constructor std::string strFoo = "Foo"; // Calls std::string(const char*) constructor // This is a valid (and standard) compiler optimization. std::string strFoo; // Calls std::string() default constructor strFoo = "Foo"; // Calls std::string::operator = (const char*)
当你有非平凡的默认构造函数时,后一种构造可能会稍微低效.
在C++标准,部分8.5,第14点的状态:
否则(即,对于剩余的复制初始化情况),创建临时.列举了可以从源类型转换为目标类型或其派生类的用户定义的转换序列(13.3.1.4),并且通过重载解析(13.3)选择最佳的转换序列.调用如此选择的用户定义转换,将初始化表达式转换为临时表达式,其类型是用户定义的转换函数调用返回的类型,具有目标类型的cv限定符.如果转换不能完成或不明确,则初始化是错误的.然后根据上述规则从临时对象初始化初始化的对象.87)在某些情况下,允许实现通过直接初始化对象来消除临时对象; 见12.2.
第12.2节的一部分规定:
即使在避免创建临时对象时,也必须遵守所有语义限制,就像创建临时对象一样.[示例:即使未调用复制构造函数,也应满足所有语义限制,例如可访问性(11).]
我只是觉得需要另一个愚蠢的帖子.
string str1 = "foo";
被称为复制初始化,因为编译器的作用是,如果它不消除任何临时值,则:
string str1(string("foo"));
除了检查所使用的转换构造函数是否隐式之外.实际上,所有隐式转换都是由标准在复制初始化方面定义的.据说,从类型U到类型T的隐式转换是有效的,如果
T t = u; // u of type U
已验证.
相反,
string str1("foo");
正在完成所写的内容,称为直接初始化.它也适用于显式构造函数.
顺便说一句,您可以使用-fno-elide-constructors禁用临时删除:
-fno-elide-constructors The C++ standard allows an implementation to omit creating a temporary which 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.
该标准表示实际上没有区别
T a = u;
和
T a(u);
如果T和u的类型是原始类型.所以你可以使用两种形式.我认为只是它的风格才能让人们使用第一种形式而不是第二种形式.
有些人可能会在某些情况下使用第一种,因为他们想要消除声明的歧义:
T u(v(a));
migh看某人作为变量的定义,该变量u
使用一个类型的临时函数进行初始化,该类型v
为其构造函数调用了一个参数a
.但事实上,编译器使用的是:
T u(v a);
它创建了一个函数声明,它接受一个类型v
的参数,并带有一个名为的参数a
.所以人们这样做
T u = v(a);
消除歧义,即使他们可以做到
T u((v(a)));
因为函数参数周围没有括号,编译器会把它读作变量定义而不是函数声明:)