执行这两个 C++初始化语法在语义上有什么不同吗?
假设以下代码是可以正确编译的合法代码,T
是类型名称,x
是变量名称。
语法一:
T a(x);
语法二:
T a = x;
这两个表达式的确切语义是否有所不同?如果是的话,是在什么情况下?
如果这两个表达式确实具有不同的语义,我也很好奇标准的哪一部分讨论了这一点。
另外,如果存在特殊情况,T 是标量类型的名称(又名 int
、long
、double
等。 .),当 T 是标量类型与非标量类型时有什么区别?
Assume that the following code is legal code that compiles properly, that T
is a type name, and that x
is the name of a variable.
Syntax one:
T a(x);
Syntax two:
T a = x;
Do the exact semantics of these two expressions ever differ? If so, under what circumstances?
If these two expressions ever do have different semantics I'm also really curious about which part of the standard talks about this.
Also, if there is a special case when T is the name of a scalar type (aka, int
, long
, double
, etc...), what are the differences when T is a scalar type vs. a non-scalar type?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
是的。如果 x 的类型不是
T
,则第二个示例将扩展为T a = T(x)
。这要求T(T const&)
是公共的。第一个示例不调用复制构造函数。检查可访问性后,可以消除副本(正如托尼指出的)。但是,在检查可访问性之前,无法消除它。
Yes. If the type of x is not
T
, then the second example expands toT a = T(x)
. This requires thatT(T const&)
is public. The first example doesn't invoke the copy constructor.After the accessibility has been checked, the copy can be eliminated (as Tony pointed out). However, it cannot be eliminated before checking accessibility.
这里的区别在于隐式构造和显式构造之间,并且可能存在差异。
想象一下,有一个带有构造函数
Array(size_t length)
的类型Array
,而在其他地方,你有一个函数count_elements(const Array& array)
。这些的目的很容易理解,并且代码看起来足够可读,直到您意识到它允许您调用count_elements(2000)
。这不仅是丑陋的代码,而且还会无缘无故地在内存中分配一个 2000 个元素长的数组。此外,您可能还有其他可隐式转换为整数的类型,也允许您对这些类型运行 count_elements() ,从而以很高的效率成本给出完全无用的结果。
您想要在这里做的是将
Array(size_t length)
声明为一个显式构造函数。这将禁用隐式转换,并且Array a = 2000
将不再是合法语法。这只是一个例子。一旦您意识到
explicit
关键字的作用,就很容易想象出其他关键字。The difference here is between implicit and explicit construction, and there can be difference.
Imagine having a type
Array
with the constructorArray(size_t length)
, and that somewhere else, you have a functioncount_elements(const Array& array)
. The purpose of these are easily understandable, and the code seems readable enough, until you realise it will allow you to callcount_elements(2000)
. This is not only ugly code, but will also allocate an array 2000 elements long in memory for no reason.In addition, you may have other types that are implicitly castable to an integer, allowing you to run count_elements() on those too, giving you completely useless results at a high cost to efficiency.
What you want to do here, is declare the
Array(size_t length)
an explicit constructor. This will disable the implicit conversions, andArray a = 2000
will no longer be legal syntax.This was only one example. Once you realise what the
explicit
keyword does, it is easy to dream up others.从 2014 年 5 月 8 日起(强调我的):
因此,它们是否等效取决于实现。
8.5.11 也相关,但仅用于确认可能存在差异:
From 8.5.14 (emphasis mine):
So, whether they're equivalent is left to the implementation.
8.5.11 is also relevant, but only in confirming that there can be a difference:
T a(x)
是直接初始化,T a = x
是复制初始化。从标准来看:
区别在于复制初始化会创建一个临时对象,然后将其用于直接初始化。允许编译器避免创建临时对象:
复制初始化需要一个非显式构造函数和一个可用的复制构造函数。
T a(x)
is direct initialization andT a = x
is copy initialization.From the standard:
The difference is that copy initialization creates a temporary object which is then used to direct-initialize. The compiler is allowed to avoid creating the temporary object:
Copy initialization requires a non-explicit constructor and a copy constructor to be available.
在 C++ 中,当您编写此内容时:
编译器实际上会生成此内容,具体取决于您的代码使用的内容:
所以现在您可以看到各种构造函数的不同语义。
复制构造函数基本上复制所有非静态数据。仅当生成的代码合法且健全时才会生成它们:如果编译器在类中看到他不知道如何复制的类型(根据正常的赋值规则),则不会生成复制构造函数。这意味着,如果 T 类型不支持构造函数,或者类的公共字段之一是 const 或引用类型,则生成器将不会创建它们 - 并且代码将不会构建。模板在构建时扩展,因此如果生成的代码不可构建,它将失败。有时它会失败,而且非常神秘。
如果您在类中定义构造函数(或析构函数),则生成器不会生成默认构造函数。这意味着您可以覆盖默认生成的构造函数。您可以将它们设置为私有(默认情况下它们是公共的),您可以覆盖它们以便它们不执行任何操作(对于节省内存和避免副作用很有用)等。
In C++, when you write this:
The compiler actually generates this, depending on what your code uses:
So now you can see the different semantics of the various constructors.
The copy constructors basically copy all the non-static data. They are only generated if the resulting code is legal and sane: if the compiler sees types inside the class that he doesn't know how to copy (per normal assignment rules), then the copy constructor won't get generated. This means that if the T type doesn't support constructors, or if one of the public fields of the class is const or a reference type, for instance, the generator won't create them - and the code won't build. Templates are expanded at build time, so if the resulting code isn't buildable, it'll fail. And sometimes it fails loudly and very cryptically.
If you define a constructor (or destructor) in a class, the generator won't generate a default one. This means you can override the default generated constructors. You can make them private (they're public by default), you can override them so they do nothing (useful for saving memory and avoiding side-effects), etc.