首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >总参考成员和临时寿命

总参考成员和临时寿命
EN

Stack Overflow用户
提问于 2016-02-10 10:58:16
回答 1查看 1.2K关注 0票数 13

给定此代码示例,传递给S的临时字符串的生存期规则是什么。

代码语言:javascript
复制
struct S
{
    // [1] S(const std::string& str) : str_{str} {}
    // [2] S(S&& other) : str_{std::move(other).str} {}

    const std::string& str_;
};

S a{"foo"}; // direct-initialization

auto b = S{"bar"}; // copy-initialization with rvalue

std::string foobar{"foobar"};
auto c = S{foobar}; // copy-initialization with lvalue

const std::string& baz = "baz";
auto d = S{baz}; // copy-initialization with lvalue-ref to temporary

根据标准:

N4140 12.2 p5.1 (用N4296删除)

临时绑定到构造函数的ctor-初始化器(12.6.2)中的引用成员,直到构造函数退出为止。

N4296 12.6.2 p8

绑定到mem初始化器中的引用成员的临时表达式不正确。

因此,拥有一个像[1]这样的用户定义的构造函数显然不是我们想要的。它甚至在最新的C++14中被认为是错误的(或者是吗?)gcc和克莱尔都没有对此提出过警告。

它是否随直接聚合初始化而改变?在这种情况下,临时生命周期就会延长。

现在关于复制初始化,默认移动构造函数和引用成员声明[2]是隐式生成的。考虑到移动可能被省略,同样的规则是否适用于隐式生成的移动构造函数?

哪个a, b, c, d有有效的引用?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-02-11 09:13:26

除非有特定异常,否则将延长绑定到引用的临时对象的生存期。也就是说,如果没有这样的异常,那么生存期将被延长。

根据最近的一份草案,N4567:

延长生存期的第二个上下文是将引用绑定到临时的时候。引用绑定到的临时对象或作为引用绑定的子对象的完整对象的临时对象在引用的生存期内持续存在,但以下情况除外:

  • (5.1)绑定到函数调用(5.2.2)中的引用参数的临时对象一直保持到包含调用的完整表达式完成为止。
  • (5.2)函数返回语句(6.6.3)中绑定到返回值的临时值的生存期没有延长;临时的生存期在返回语句中的完整表达式末尾被销毁。
  • (5.3)临时绑定到新初始化器(5.3.4)中的引用的临时表达式持续到包含新初始化器的完整表达式完成为止。

对C++11的唯一重要更改是,正如OP所提到的,在C++11中,引用类型的数据成员(来自N3337)有一个额外的例外:

  • 临时绑定到构造函数的ctor-初始化器(12.6.2)中的引用成员,直到构造函数退出为止。

这是在CWG 1696 (后C++14)中删除的,现在通过mem初始化程序将临时对象绑定到引用数据成员是不正确的。

关于执行部分中的例子:

string& str_;};S{“foo”};//直接初始化

这将创建一个临时std::string并使用它初始化str_数据成员。S a{"foo"}使用聚合初始化,因此不涉及mem初始化程序。没有适用于生存期扩展的例外情况,因此该临时的生存期将扩展到引用数据成员str_的生存期。

auto b= S{"bar"};//用rvalue复制初始化

在强制使用C++17:进行复制之前,我们创建一个临时std::string,通过将临时std::string绑定到str_引用成员来初始化临时S。然后,我们将临时的S移到b中。这将“复制”引用,这不会延长std::string临时的生存期。但是,实现将避免从临时S转移到b。但是,这不能影响临时std::string的生存期。您可以在以下程序中观察到这一点:

代码语言:javascript
复制
#include <iostream>

#define PRINT_FUNC() { std::cout << __PRETTY_FUNCTION__ << "\n"; }

struct loud
{
    loud() PRINT_FUNC()
    loud(loud const&) PRINT_FUNC()
    loud(loud&&) PRINT_FUNC()
    ~loud() PRINT_FUNC()
};

struct aggr
{
    loud const& l;
    ~aggr() PRINT_FUNC()
};

int main() {
    auto x = aggr{loud{}};
    std::cout << "end of main\n";
    (void)x;
}

现场演示

请注意,loud的析构函数在“main的结束”之前调用,而x则一直保存到跟踪之后。形式上,临时loud在创建它的完整表达式的末尾被销毁.

如果aggr的移动构造函数是用户定义的,则行为不会改变。

在C++17:中强制复制,我们识别rhs S{"bar"}上的对象和lhs b上的对象。这将导致将临时生命期扩展到b的生存期。见CWG 1697

对于剩下的两个例子,move构造函数--如果被调用--只复制引用。当然,移动构造函数( S)可以省略,但这是不可观察的,因为它只复制引用。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35313292

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档