std::initializer_list 在 clang 范围结束之前破坏包含的对象
std::initializer_list destructs the containing objects before the scope end in clang
如以下代码所示,std::initializer_list 的包含对象甚至在范围完成之前就被销毁了。我想知道代码是否不符合标准,或者它是 clang 中的错误(因为它与 gcc 和 vc 一起工作得很好)。
#include <iostream>
class Attrib
{
public:
Attrib()
{
std::cout<< "Constructor called"<< std::endl;
}
Attrib(const Attrib& r)
{
std::cout<< "Copy constructor called"<< std::endl;
}
~Attrib()
{
std::cout<< "Destructor called"<< std::endl;
}
};
int main()
{
auto attribs = std::initializer_list<Attrib>({Attrib()});
std::cout<< "Before returning from main"<< std::endl;
return 0;
}
Constructor called
Destructor called
Before returning from main
Constructor called
Before returning from main
Destructor called
编辑:
如果我们像这样修改 main 中的 initializer_list 创建行,
std::initializer_list<Attrib> attribs = {Attrib()};
Constructor called
Before returning from main
Destructor called
错误在您的程序中。 std::initializer_list
不需要对其赋值运算符执行深度复制,它是一个轻量级代理对象。
因此,从语义上讲,您的 Attrib
对象在行 auto attribs = std::initializer_list...
结束后立即被销毁(当它们的临时对象被销毁时)。所以 clang
在语义上是正确的。
gcc 为您提供不同输出的原因很可能是由于启用了不同的优化。
这是 Clang 中的一个错误。可能 31636 or 40562.
auto attribs = std::initializer_list<Attrib>({Attrib()});
这是从初始化程序 std::initializer_list<Attrib>({Attrib()})
复制初始化 attribs
。按照 C++17 保证复制省略,这相当于从 {Attrib()}
.
复制初始化 attribs
¶5 An object of type std::initializer_list<E>
is constructed from an initializer list as if the implementation generated and materialized a prvalue of type "array of N const E
", where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E>
object is constructed to refer to that array.
¶6 The array has the same lifetime as any other temporary object, except that initializing an initializer_list
object from the array extends the lifetime of the array exactly like binding a reference to a temporary.
根据CWG1565:
Q. If an initializer_list
object is copied and the copy is elided, is the lifetime of the underlying array object extended?
A. The consensus of CWG was that the behavior should be the same, regardless of whether the copy is elided or not.
正如您在编辑中所观察到的,当我们删除多余的“副本”时,Clang 的行为是正确的:
auto attribs = std::initializer_list<Attrib>{Attrib()};
如以下代码所示,std::initializer_list 的包含对象甚至在范围完成之前就被销毁了。我想知道代码是否不符合标准,或者它是 clang 中的错误(因为它与 gcc 和 vc 一起工作得很好)。
#include <iostream>
class Attrib
{
public:
Attrib()
{
std::cout<< "Constructor called"<< std::endl;
}
Attrib(const Attrib& r)
{
std::cout<< "Copy constructor called"<< std::endl;
}
~Attrib()
{
std::cout<< "Destructor called"<< std::endl;
}
};
int main()
{
auto attribs = std::initializer_list<Attrib>({Attrib()});
std::cout<< "Before returning from main"<< std::endl;
return 0;
}
Constructor called
Destructor called
Before returning from main
Constructor called
Before returning from main
Destructor called
编辑: 如果我们像这样修改 main 中的 initializer_list 创建行,
std::initializer_list<Attrib> attribs = {Attrib()};
Constructor called
Before returning from main
Destructor called
错误在您的程序中。 std::initializer_list
不需要对其赋值运算符执行深度复制,它是一个轻量级代理对象。
因此,从语义上讲,您的 Attrib
对象在行 auto attribs = std::initializer_list...
结束后立即被销毁(当它们的临时对象被销毁时)。所以 clang
在语义上是正确的。
gcc 为您提供不同输出的原因很可能是由于启用了不同的优化。
这是 Clang 中的一个错误。可能 31636 or 40562.
auto attribs = std::initializer_list<Attrib>({Attrib()});
这是从初始化程序 std::initializer_list<Attrib>({Attrib()})
复制初始化 attribs
。按照 C++17 保证复制省略,这相当于从 {Attrib()}
.
attribs
¶5 An object of type
std::initializer_list<E>
is constructed from an initializer list as if the implementation generated and materialized a prvalue of type "array of Nconst E
", where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and thestd::initializer_list<E>
object is constructed to refer to that array.¶6 The array has the same lifetime as any other temporary object, except that initializing an
initializer_list
object from the array extends the lifetime of the array exactly like binding a reference to a temporary.
根据CWG1565:
Q. If an
initializer_list
object is copied and the copy is elided, is the lifetime of the underlying array object extended?A. The consensus of CWG was that the behavior should be the same, regardless of whether the copy is elided or not.
正如您在编辑中所观察到的,当我们删除多余的“副本”时,Clang 的行为是正确的:
auto attribs = std::initializer_list<Attrib>{Attrib()};