我们知道这T v(x);
被称为直接初始化,而T v = x;
被称为复制初始化,这意味着它将构造一个临时T
的x
,将被复制/移动到v
(很可能被省略)。
对于列表初始化,标准根据上下文区分两种形式。T v{x};
称为直接列表初始化,而T v = {x};
称为复制列表初始化:
§8.5.4 [dcl.init.list] p1
[...] 列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为直接列表初始化,复制初始化上下文中的列表初始化称为复制列表初始化。[...]
但是,整个标准中只有两个参考文献。对于直接列表初始化,在创建T{x}
( §5.2.3/3
) 之类的临时对象时会提到它。对于复制列表初始化,它用于返回语句中的表达式,如return {x};
( §6.6.3/2
)。
现在,下面的代码片段呢?
#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
定义为delete
d。但是,最新版本的 Clang 和 GCC 可以很好地编译上面的代码,并且在挖掘了一下(并找到上面的引用)之后,这似乎是正确的行为。该标准只定义了整个列表初始化的行为,除了上述几点之外,根本不区分这两种形式。好吧,至少据我所知,无论如何。
所以,再次总结我的问题:
如果它们(显然)做完全相同的事情,那么将列表初始化分成两种形式有什么用?