gpt4 book ai didi

c++ - 聚合引用成员和临时生命周期

转载 作者:IT老高 更新时间:2023-10-28 23:20:53 25 4
gpt4 key购买 nike

鉴于此代码示例,关于传递给 S 的临时字符串的生命周期的规则是什么。

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 中删除)

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

N4296 12.6.2 p8

A temporary expression bound to a reference member in a mem-initializer is ill-formed.

因此,像 [1] 这样的用户定义构造函数绝对不是我们想要的。在最新的 C++14 中,它甚至应该是格式错误的(或者是吗?)gcc 和 clang 都没有警告过它。
它会随着直接聚合初始化而改变吗?我看起来在那种情况下,临时生命周期延长了。

现在关于复制初始化,Default move constructor and reference members声明 [2] 是隐式生成的。鉴于移动可能会被省略,同样的规则是否适用于隐式生成的移动构造函数?

a、b、c、d 中哪一个具有有效引用?

最佳答案

除非有特定的异常(exception),否则绑定(bind)到引用的临时对象的生命周期会延长。也就是说,如果没有这个异常,那么生命周期就会被延长。

来自最近的草稿,N4567:

The second context [where the lifetime is extended] is when areference is bound to a temporary. The temporary to which thereference is bound or the temporary that is the complete object of asubobject to which the reference is bound persists for the lifetime ofthe reference except:

  • (5.1) A temporary object bound to a reference parameter in a function call (5.2.2) persists until the completion of thefull-expression containing the call.
  • (5.2) The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary isdestroyed at the end of the full-expression in the return statement.
  • (5.3) A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expressioncontaining the new-initializer.

正如 OP 所述,C++11 的唯一重大变化是,在 C++11 中,引用类型的数据成员有一个额外的异常(exception)(来自 N3337):

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

这已在 CWG 1696(C++14 后)中删除,现在通过 mem-initializer 将临时对象绑定(bind)到引用数据成员的格式不正确。


关于 OP 中的示例:

struct S
{
const std::string& str_;
};

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

这会创建一个临时的 std::string 并用它初始化 str_ 数据成员。 S a{"foo"} 使用聚合初始化,因此不涉及 mem-initializer。没有适用于生命周期延长的异常(exception),因此该临时的生命周期被延长到引用数据成员 str_ 的生命周期。


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

在使用 C++17 强制复制省略之前:形式上,我们创建一个临时 std::string,通过将临时 std::string 绑定(bind)到 str_ 来初始化一个临时 S 引用成员。然后,我们将该临时 S 移动到 b 中。这将“复制”引用,这不会延长 std::string 临时文件的生命周期。但是,实现会省略从临时 Sb 的移动。但是,这一定不会影响临时 std::string 的生命周期。您可以在以下程序中观察到这一点:

#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;
}

Live demo

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

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

在 C++17 中强制复制省略:我们将 rhs S{"bar"} 上的对象与 lhs 上的对象识别b。这会导致临时文件的生命周期延长到 b 的生命周期。见 CWG 1697


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

关于c++ - 聚合引用成员和临时生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35313292/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com