为什么标准区分直接列表初始化和复制列表初始化? [英] Why does the standard differentiate between direct-list-initialization and copy-list-initialization?
问题描述
我们知道 T v(x);
被称为直接初始化,而 T v = x; 被称为复制初始化,意味着它将从
x 构造一个临时
T
/ code>将被复制/移动到 v
(这很可能被省略)。
We know that T v(x);
is called direct-initialization, while T v = x;
is called copy-initialization, meaning that it will construct a temporary T
from x
that will get copied / moved into v
(which is most likely elided).
对于列表初始化,标准区分两种形式,具体取决于上下文。 T v {x}; 被称为直接列表初始化 c $ c>被称为 copy-list-initialization :
For list-initialization, the standard differentiates between two forms, depending on the context. T v{x};
is called direct-list-initialization while T v = {x};
is called copy-list-initialization:
§8.5.4[dcl.init.list ] p1
[...]列表初始化可以发生在直接初始化或复制初始化
contexts;在直接初始化上下文中的列表初始化被称为直接列表初始化,并且在复制初始化上下文中的列表初始化被称为复制列表初始化。 [...]
[...] List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization. [...]
但是,在整个标准中只有两个引用。对于直接列表初始化,在创建 T {x}
(§5.2.3/ 3
)。对于复制列表初始化,它是在 return {x};
(§6.6.3/ 2
)。
However, there are only two more references each in the whole standard. For direct-list-initialization, it's mentioned when creating temporaries like T{x}
(§5.2.3/3
). For copy-list-initialization, it's for the expression in return statements like return {x};
(§6.6.3/2
).
现在,以下代码片段如何?
Now, what about the following snippet?
#include <initializer_list>
struct X{
X(X const&) = delete; // no copy
X(X&&) = delete; // no move
X(std::initializer_list<int>){} // only list-init from 'int's
};
int main(){
X x = {42};
}
通常,从 X x = expr;
模式,我们期望代码无法编译,因为 X
的move构造函数定义为 delete
d。但是,最新版本的Clang和GCC编译上面的代码就好了,在挖了一下(找到上面的引号)后,这似乎是正确的行为。标准只定义了整个列表初始化的行为,除了上面提到的点之外,根本不区分两种形式。
Normally, from the X x = expr;
pattern, we expect the code to fail to compile, because the move constructor of X
is defined as delete
d. However, the latest versions of Clang and GCC compile the above code just fine, and after digging a bit (and finding the above quote), that seems to be correct behaviour. The standard only ever defines the behaviour for the whole of list-initialization, and doesn't differentiate between the two forms at all except for the above mentioned points. Well, atleast as far as I can see, anyways.
因此,我再次总结一下我的问题:
So, to summarize my question again:
如果它们(显然)执行完全相同的事情,将列表初始化拆分为两种形式的用途是什么?
推荐答案
因为他们不会做完全相同的事情。如13.3.1.7中所述[over.match.list]:
Because they don't do the exact same thing. As stated in 13.3.1.7 [over.match.list]:
在复制列表初始化中,如果选择显式构造函数,
In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.
简而言之,您只能在复制列表初始化上下文中使用隐式转换。
In short, you can only use implicit conversion in copy-list-initialization contexts.
这是明确添加的,使统一初始化不,um,uniform。
This was explicitly added to make uniform initialization not, um, uniform. Yeah, I know how stupid that sounds, but bear with me.
在2008年, N2640已发布(PDF),查看统一初始化的当前状态。它仔细查看了直接初始化( T {...}
)和复制初始化( T = {...} code>)。
In 2008, N2640 was published (PDF), taking a look at the current state of uniform initialization. It looked specifically at the difference between direct initialization (T{...}
) and copy-initialization (T = {...}
).
总而言之,关注的是显式
构造函数会变得毫无意义。如果我有一些类型 T
,我想能够从一个整数构造,但我不想隐式转换,我标记构造函数显式。
To summarize, the concern was that explicit
constructors would effectively become pointless. If I have some type T
that I want to be able to be constructed from an integer, but I don't want implicit conversion, I label the constructor explicit.
然后有人这样做:
T func()
{
return {1};
}
没有当前的写法,这将调用我的 explicit
构造函数。那么,如果它不改变太多,使构造函数显式有什么好处呢?
Without the current wording, this will call my explicit
constructor. So what good is it to make the constructor explicit
if it doesn't change much?
,您需要至少直接使用该名称:
With the current wording, you need to at least use the name directly:
T func()
{
return T{1};
}
这篇关于为什么标准区分直接列表初始化和复制列表初始化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!