c – 通过rvalue数据成员扩展临时生命周期,而不是使用构造函数,为什么?

我找到了以下方案来延长临时终身作品,我不知道是否应该,但它是.
struct S {
    std::vector<int>&& vec;
};

int main() {
    S s1{std::vector<int>(5)};      // construct with temporary
    std::cout << s1.vec[0] << '\n'; // fine,temporary is alive
}

但是,当S被赋予一个显式的值构造函数时,它不再是一个聚合,并且该方案在s1.vec [0]上无效读取失败,

struct S {
    std::vector<int>&& vec;
    S(std::vector<int>&& v)
        : vec{std::move(v)}         // bind to the temporary provided
    { }
};

int main() {
    S s1{std::vector<int>(5)};      // construct with temporary
    std::cout << s1.vec[0] << '\n'; // not ok. invalid read on free'd memory
}

为什么这是有效的聚合?我认为它与构造函数一个实际的函数调用有关,基于我用const lvalue引用的红色.另外,有什么办法可以使后一种情况发挥作用吗?

在SO上使用lvalue引用的类似情况有很多问题.我看到,如果我使用了一个const lvalue参考,它不会有助于延长临时的生命周期,rvalue引用的规则是否相同?

解决方法

TL; DR

可以使用聚合初始化来延长临时性的使用寿命,因为用户定义的构造函数实际上是一个函数调用,所以不能这样做.

注意:T const&和T&&适用于总体生命力的情况,延长对他们的临时生活.

什么是聚合?

struct S {                // (1)
  std::vector<int>&& vec;
};

为了回答这个问题,我们必须深入了解一个聚合的初始化和一个类类型的初始化之间的区别,但首先我们必须确定一个聚合是什么:

8.5.1p1 Aggregates [dcl.init.aggr]

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1),no private or protected non-static data members (Clause 11),no base classes (Clause 10),and no virtual functions (10.3)

注意:上述表示(1)是聚合.

如何初始化聚合?

聚合和“非聚合”之间的初始化差异很大,这里直接来自标准:

8.5.1p2 Aggregates [dcl.init.aggr]

When an aggregate is initialized by an initializer list,as specified in 8.5.4,the elements of the initializer list are taken as initializers for the members of the aggregate,in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause.

上述引用表明,我们正在初始化器子句中初始化器初始化我们的聚合成员,之间没有任何一个步骤.

struct A { std::string a; int b; };
A x { std::string {"abc"},2 };

从语义上来说,上面相当于使用下面的初始化我们的成员,只是这种情况下的A :: a和A :: b只能通过x.a和x.b访问.

std::string A::a { std::string {"abc"} };
int         A::b { 2 };

如果我们将A :: a的类型更改为rvalue引用或const lvalue引用,我们将直接将初始化的临时用法绑定到x.a.

rvalue引用的规则和const lvalue引用表明,临时生存期将被扩展到主机的生命周期,这正是将要发生的事情.

使用用户声明的构造函数的初始化如何不同?

struct S {                    // (2)
    std::vector<int>&& vec;
    S(std::vector<int>&& v)
        : vec{std::move(v)}   // bind to the temporary provided
    { }
};

一个构造函数真的只是一个花哨的函数,用于初始化一个类实例.适用于功能的相同规则适用于它们.

当涉及延长临时人员的终身时间时,没有任何区别.

std::string&& func (std::string&& ref) {
  return std::move (ref);
}

临时传递给func将不会延长其生命周期,因为我们有一个声明为rvalue / lvalue引用的参数.即使我们返回“相同”的引用,以便它在func之外可用,它不会发生.

这是在(2)的构造函数中发生的,毕竟构造函数只是一个用于初始化对象的“花式函数”.

12.2p5 Temporary objects [class.temporary]

The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:

  • A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.

  • A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.

  • The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.

    • A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expression containing the new-initializer.

注意:请注意,通过新的T {…}的聚合初始化与上述规则不同.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


对象的传值与返回说起函数,就不免要谈谈函数的参数和返回值。一般的,我们习惯把函数看作一个处理的封装(比如黑箱),而参数和返回值一般对应着处理过程的输入和输出。这种情况下,参数和返回值都是值类型的,也就是说,函数和它的调用者的信息交流方式是用过数据的拷贝来完成,即我们习惯上称呼的“值传递”。但是自从引
从实现装饰者模式中思考C++指针和引用的选择最近在看设计模式的内容,偶然间手痒就写了一个“装饰者”模式的一个实例。该实例来源于风雪涟漪的博客,我对它做了简化。作为一个经典的设计模式,本身并没有太多要说的内容。但是在我尝试使用C++去实现这个模式的实例的时候,出现了一些看似无关紧要但是却引人深思的问题
关于vtordisp知多少?我相信不少人看到这篇文章,多半是来自于对标题中“vtordisp”的好奇。其实这个关键词也是来源于我最近查看对象模型的时候偶然发现的。我是一个喜欢深究问题根源的人(有点牛角尖吧),所以当我第一次发现vtordisp的时候,我也是很自然的把它输进google查找相关资料,但
那些陌生的C++关键字学过程序语言的人相信对关键字并不陌生。偶然间翻起了《C++ Primer》这本书,书中列举了所有C++的关键字。我认真核对了一下,竟然发现有若干个从未使用过的关键字。一时间对一个学了六年C++的自己狠狠鄙视了一番,下决心一定要把它们搞明白!图1红色字体给出的是我个人感觉一般大家
命令行下的树形打印最近在处理代码分析问题时,需要将代码的作用域按照树形结构输出。问题的原型大概是下边这个样子的。图中给了一个简化的代码片段,该代码片段包含5个作用域:全局作用域0、函数fun作用域1、if语句作用域2、else语句作用域3和函数main作用域4。代码作用域有个显著的特点就是具有树形结
虚函数与虚继承寻踪封装、继承、多态是面向对象语言的三大特性,熟悉C++的人对此应该不会有太多异议。C语言提供的struct,顶多算得上对数据的简单封装,而C++的引入把struct“升级”为class,使得面向对象的概念更加强大。继承机制解决了对象复用的问题,然而多重继承又会产生成员冲突的问题,虚继
不要被C++“自动生成”所蒙骗C++对象可以使用两种方式进行创建:构造函数和复制构造函数。假如我们定义了类A,并使用它创建对象。Aa,b;Ac=a;Ad(b);对象a和b使用编译器提供的默认构造函数A::A()创建出来,我们称这种创建方式为对象的定义(包含声明的含义)。对象c和d则是使用已有的对象,
printf背后的故事 说起编程语言,C语言大家再熟悉不过。说起最简单的代码,Helloworld更是众所周知。一条简单的printf语句便可以完成这个简单的功能,可是printf背后到底做了什么事情呢?可能很多人不曾在意,也或许你比我还要好奇!那我们就聊聊printf背后的故事。 一、printf
定义 浮点数就是小数点位置不固定的数,也就是说与定点数不一样,浮点数的小数点后的小数位数可以是任意的,根据IEEE754-1985(也叫IEEE Standard for Binary Floating-Point Arithmetic)的定义,浮点数的类型有两种:单精度类型(用4字节存储)和双精度
在《从汇编看c++的引用和指针》一文中,虽然谈到了引用,但是只是为了将两者进行比较。这里将对引用做进一步的分析。1 引用的实现方式在介绍有关引用的c++书中,很多都说引用只是其引用变量的一个别名。我自己不是很喜欢这种解释,因为觉得这种解释会给人误解,好像引用和变量就是一回事,而且,书中也没有给出,为
今天写程序的时候,创建了一个结构体:struct BufferObj {char* buf;int bufLen;SOCKADDR_STORAGE addr;int addrLen;struct BufferObj* next;};该结构体有一个next指针,本意是这个指针初始的时候应该为NULL,
placement operator new是重载的operator new运算符,它允许我们将对象放到一个指定的内存中。下面来看c++源码:class X {private: int _x;public: X(int xx = 0) : _x(xx) {} ~X() {} void* operat
编码的目的,就是给抽象的字符赋予一个数值,好在计算机里面表示。常见的ASCII使用8bit给字符编码,但是实际只使用了7bit,最高位没有使用,因此,只能表示128个字符;ISO-8859-1(也叫Latin-1,或者直接8859)使用全8bit编码,可以看成是ASCII的超集,因为它的低128个字
在宏定义当中,常常可以看到宏的参数以及整个宏的定义都被小括号包围,就像下面的 MIN、MAX、ABS 宏一样: 上面的图截取自 iOS 的系统库,那为什么它们需要这些括号包围起来呢? 下面假如我们自定义了宏 ceil_div,代码如下: #define ceil_div(x, y) (x + y -
c++中,当继承结构中含有虚基类时,在构造对象时编译器会通过将一个标志位置1(表示调用虚基类构造函数),或者置0(表示不调用虚基类构造函数)来防止重复构造虚基类子对象。如下图菱形结构所示:当构造类Bottom对象时,Bottom构造函数里面的c++伪码如下(单考虑标志位,不考虑其他)://Botto
在C中,使用fopen打开文件有两种模式:一种是文本模式,一种是二进制模式。那这两种模式之间有什么区别,是不是使用文本模式打开的文件就只能使用文本函数比如fprintf来操作,而使用二进制打开的文件就只能使用二进制函数比如fwrite来操作呢? 答案是否定的。C里面之所以有文本模式和二进制模式,完全
尾数英文名叫mantissa,significand,coefficient,用于科学计数法中。科学计数法的表示方法为: Mantissa x Base^Exponent 举个例子,123.45用科学计数法可以表示为: 12345 x 10^(-2) 其中12345就是尾数Mantissa,10是基
定义宏时可以让宏接收可变参数,对于可变参数的定义,标准 C 和 GNU C(GNU 对 C的扩展)是不一样的。 标准 C 标准 C 对于可变参数的定义如下,使用...: #define eprintf(...) fprintf (stderr, __VA_ARGS__) 在宏定义中,__VA_ARG
宏分为两种,一种是 object-like 宏,比如: #define STR &quot;Hello, World!&quot; 另一种是 function-like 宏,比如: #define MIN(X, Y) ((X) &lt; (Y) ? (X) : (Y)) 对于 function-li
副作用(Side Effect) 在计算机当中,副作用指当调用一个函数时,这个函数除了返回一个值之外,还对主调函数产生了影响,比如修改了全局变量,修改了参数等等。 宏的重复副作用 对于求两个数中的最小数,常常可以定义一个宏 MIN,定义如下: #define MIN(X, Y) ((X) &lt;