- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
GCC 的实现破坏了 std::initializer_list
从返回完整表达式末尾的函数返回的数组。这是正确的吗?
这个程序中的两个测试用例都显示了在值可以使用之前执行的析构函数:
#include <initializer_list>
#include <iostream>
struct noisydt {
~noisydt() { std::cout << "destroyed\n"; }
};
void receive( std::initializer_list< noisydt > il ) {
std::cout << "received\n";
}
std::initializer_list< noisydt > send() {
return { {}, {}, {} };
}
int main() {
receive( send() );
std::initializer_list< noisydt > && il = send();
receive( il );
}
我认为该程序应该可以运行。但是底层的标准语有点复杂。
return 语句初始化一个返回值对象,就像它被声明一样
std::initializer_list< noisydt > ret = { {},{},{} };
这会初始化一个临时的 initializer_list
及其来自给定系列初始化器的底层数组存储,然后初始化另一个 initializer_list
从第一个。阵列的生命周期是多少? “数组的生命周期与 initializer_list
对象的生命周期相同。”但是其中有两个;哪一个是模棱两可的。 8.5.4/6 中的示例,如果它像宣传的那样工作,应该解决数组具有复制到对象的生命周期的歧义。然后返回值的数组也应该存在于调用函数中,并且应该可以通过将其绑定(bind)到命名引用来保存它。
开启 LWS , GCC 在返回之前错误地杀死了数组,但它保留了一个名为 initializer_list
根据示例。 Clang 也可以正确处理示例,但列表中的对象从不 销毁;这会导致内存泄漏。 ICC 不支持 initializer_list
完全没有。
我的分析正确吗?
C++11 §6.6.3/2:
A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list.
8.5.4/1:
… list-initialization in a copy-initialization context is called copy-list-initialization.
8.5/14:
The initialization that occurs in the form
T x = a;
… is called copy-initialization.
回到 8.5.4/3:
List-initialization of an object or reference of type T is defined as follows: …
— Otherwise, if T is a specialization of
std::initializer_list<E>
, aninitializer_list
object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (8.5).
8.5.4/5:
An object of type
std::initializer_list<E>
is constructed from an initializer list as if the implementation allocated an array of N elements of type 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. If a narrowing conversion is required to initialize any of the elements, the program is ill-formed.
8.5.4/6:
The lifetime of the array is the same as that of the
initializer_list
object. [Example:typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };
void f() {
std::vector<cmplx> v2{ 1, 2, 3 };
std::initializer_list<int> i3 = { 1, 2, 3 };
}For
v1
andv2
, theinitializer_list
object and array createdfor{ 1, 2, 3 }
have full-expression lifetime. Fori3
, the initializer_list object and array have automatic lifetime. — end example]
当你返回一个用大括号括起来的裸列表时,
A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list.
这并不意味着返回到调用范围的对象是从某物复制而来的。例如,这是有效的:
struct nocopy {
nocopy( int );
nocopy( nocopy const & ) = delete;
nocopy( nocopy && ) = delete;
};
nocopy f() {
return { 3 };
}
这不是:
nocopy f() {
return nocopy{ 3 };
}
复制列表初始化仅仅意味着语法nocopy X = { 3 }
的等价物。用于初始化表示返回值的对象。这不会调用拷贝,它恰好与 8.5.4/6 中延长数组生命周期的示例相同。
Clang 和 GCC 会做 agree关于这一点。
对 N2640 的评论没有提到这个极端案例。已经对此处组合的各个功能进行了广泛的讨论,但我看不到它们之间的交互。
实现这一点很麻烦,因为它归结为按值返回一个可选的可变长度数组。因为 std::initializer_list
不拥有其内容,该函数还必须返回其他内容。当传递给函数时,这只是一个本地的、固定大小的数组。但在另一个方向,VLA 需要与 std::initializer_list
一起返回到堆栈中。的指针。然后需要告知调用者是否处理序列(无论它们是否在堆栈上)。
这个问题很容易通过从 lambda 函数返回一个花括号初始化列表来偶然发现,这是一种“自然”的方式来返回一些临时对象而不关心它们是如何包含的。
auto && il = []() -> std::initializer_list< noisydt >
{ return { noisydt{}, noisydt{} }; }();
确实,这与我到达这里的方式相似。但是,省略 ->
将是错误的。 trailing-return-type,因为 lambda 返回类型推导只发生在返回表达式时,而花括号初始化列表不是表达式。
最佳答案
std::initializer_list
不是容器,不要使用它来传递值并期望它们持续存在
DR 1290更改了措辞,您还应该注意1565和 1599还没准备好。
Then the return value's array should also survive into the calling function, and it should be possible to preserve it by binding it to a named reference.
不,这不符合。数组的生命周期不会随着 initializer_list
一起延长。考虑:
struct A {
const int& ref;
A(const int& i = 0) : ref(i) { }
};
引用 i
绑定(bind)到临时 int
,然后引用 ref
也绑定(bind)到它,但这并没有扩展i
的生命周期,它仍然在构造函数的末尾超出范围,留下一个悬空引用。您不会通过绑定(bind)另一个引用来延长底层临时的生命周期。
如果 1565,您的代码可能会更安全被批准并且您将 il
制作为拷贝而不是引用,但该问题仍然存在,甚至没有建议的措辞,更不用说实现经验了。
即使您的示例旨在工作,关于底层数组生命周期的措辞显然仍在改进,编译器需要一段时间才能实现最终确定的语义。
关于c++ - std::initializer_list 返回值的生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15286450/
我需要使用 initializer_list 来初始化编译时大小的类数组。我已经知道我可以使用参数包构造函数并当场初始化它,但在这种情况下我需要使用 initializer_list。如果可能,我还想
我有一个类构造函数接受一个 initializer_list 这个构造函数必须运行接受一个的父类构造函数 initializer_list>. 所以我必须将初始化列表转换为二维初始化列表。 {1, 2
这个问题在这里已经有了答案: 关闭10 年前。 Possible Duplicate: initializer_list and move semantics 环境:Linux,g++-4.7 我使
以下代码编译并运行: #include #include #include #include void ext( std::initializer_list >> myList ) {
std::vector的初始化列表构造函数具有以下形式 vector( std::initializer_list init, const Allocator& alloc = Allocator()
考虑以下代码片段... void boo(std::initializer_list l) { } template void foo(std::initializer_list l) {
我的问题是关于 std::initializer_list 之间缺乏转换当这些转换看起来很容易实现时,包含的类型或多或少是 cv 限定的类型。 考虑以下无效代码: std::initializer_l
我的图表构造函数: Graph(std::initializer_list list); 我的边缘构造函数: Edge(int out, int in); 我想通过以下方式创建我的图表: Graph
我有一些我想在编译时由需要某种程度验证的初始化列表初始化的类。 我首先尝试使用static_assert,但不会与错误“静态声明的非恒定条件”一起编译 造成此错误的最佳方法是什么? class foo
//parameter pack sum example constexpr int sum(int N= 0) { return N; } template constexpr int su
我是新来的,这是我的第一个问题。 所以,我有这个功能: std::string join(string_initializer_list p_input) const { std::strin
想问一下有没有机会补上引用功能。假设我有功能: double refce( double (&f1)(double), double in ){ return f1(in); } 而不是这样调
所以我昨天尝试开始使用 std::initializer_list 但这并不是一个巨大的成功。这是我最后的尝试之一: #include #include struct XmlState
考虑函数: template void printme(T&& t) { for (auto i : t) std::cout ({'a', 'b', 'c'})); printme(st
考虑函数: template void printme(T&& t) { for (auto i : t) std::cout ({'a', 'b', 'c'})); printme(st
我想用 std::initializer_list 初始化基类。 struct A : public std::array { // This constructor works fine A
基于这段代码 struct Foo { Foo() { cout ilist) { cout 构造函数的大括号初始化中。 相反,复制构造函数带头。 你
尝试为 Node 安装 phash-image 但出现此错误: > phash-image@3.1.0 install /Users/jong/Workspace/mgmtio/phash-image
下面对 foo 的调用是否有效? GCC 似乎对此很满意,而 Clang 为 foo 给出了“无匹配函数”错误;以及无法推断出 N 的注释。 template void foo(const int
我正在尝试使用函数模板 foo 将参数转换为 initializer_list。但是,它转换的 initializer_list 具有与输入参数不同的奇怪值。 #include #include
我是一名优秀的程序员,十分优秀!