简体   繁体   English

返回使用braced init list初始化的对象时,我保证会有一对构造函数和析构函数调用吗?

[英]Am I guaranteed a single pair of constructor and destructor calls when returning an object initialized with braced init list?

Take the following class for example 以下面的课程为例

#include <iostream>

using namespace std;

class A{
private:
    int a_;
    int b_;

    A(const A&) = delete;
    A& operator=(const A&) = delete;
    A(A&&) = delete;
    A& operator=(A&&) = delete;

public:
    A(int a, int b) : a_{a}, b_{b}{cout<<"constructed\n";}

    void ephemeral() const{cout<<"operation\n";}

    ~A(){cout<<"destructed\n";}

};

A make_A(int a, int b){
    return {a, b};
}

int main(){
    make_A(1, 2).ephemeral();
    return 0;
}

It gives the expected result . 它给出了预期的结果

The object is constructed, the operation is performed, then it is destructed. 构造对象,执行操作,然后对其进行破坏。

However, I'm concerned whether this is guaranteed. 但是,我担心这是否有保证。 My main concern is whether I could be seeing any effects I'm unaware of due to freedom given to the compiler by the standard. 我主要担心的是,由于标准给予编译器的自由,我是否能看到任何我不知道的影响。

I don't think copy-elision is a factor here because all move and copy constructors are declared deleted so how could they be called? 我不认为copy-elision是一个因素,因为所有的移动和复制构造函数都被声明删除,所以它们怎么能被调用?

The only constructor being called is the one that takes two integers. 被调用的唯一构造函数是带有两个整数的构造函数。 Can I be sure that this will behave consistently across compilers, platforms, and optimization levels? 我可以确定这在编译器,平台和优化级别上的表现是否一致?

I suspect that the answer is 'yes' but there could be subtleties. 我怀疑答案是肯定的,但可能会有微妙之处。

When you return {a,b}; 当你return {a,b}; you directly construct the return value. 你直接构造了返回值。

No temporary, logical or otherwise, is created. 不会创建临时的,逻辑的或其他的。 No elision occurs. 不会出现任何缺陷。

This return value is available in the returned context in main . 此返回值在main返回的上下文中可用。 You can call its .ephemeral() operation. 您可以调用它的.ephemeral()操作。 At the end of the full-expression, it goes out of scope, unless you "store" it in a A const& (reference lifetime extension kicks in) or in a A&& (ditto) or in an auto const& or auto&& variable like this: 在完整表达式结束时,它超出范围,除非您将它“存储”在A const& (参考生命周期扩展插入)或A&& (同上)或auto const&auto&&变量中,如下所示:

auto&& a = make_A(1, 2);
a.ephemeral();

Still, in the above case, no copy occurs. 但是,在上述情况下,不会发生复制。

None of these actions can cause a copy under the standard. 这些操作都不会导致标准下的副本。

You are correct in some cases copy construction can be elided out of existence. 在某些情况下,您是正确的,可以省略复制构造。 Elision is when two objects have their identity and lifetimes merged. Elision是指两个物体的身份和生命时间合并在一起的时候。 So if make_A read: 所以,如果make_A读取:

A make_A(int a, int b){
  A r{a,b};
  return r;
}

r could be elided into the return value. r可以被省略为返回值。 Here, however, the compiler would demand that A(A const&) or A(A&&) be defined, so it would not compile with your A . 但是,在这里,编译器会要求定义A(A const&)A(A&&) ,因此不会使用A编译。 In practice, once it checked that they are defined, it wouldn't call them because the r within make_A would be elided to be the same object as the return value of make_A . 实际上,一旦检查它们被定义,它就不会调用它们,因为make_Ar将被省略为与make_A的返回值相同的对象。

Similarly, 同样的,

A a = make_A(1,2);

the temporary returned by make_A is elided to be the same as the named variable a . make_A返回的临时make_A被省略为与命名变量a相同。 Elision is transient, so this could also elide together a variable within make_A . Elision是瞬态的,因此这也可以将make_A的变量放在 make_A In this case, you also need A(A&&) or A(A const&) to exist. 在这种情况下,您还需要存在A(A&&)A(A const&)

By deleting the move/copy ctors, they cannot be called, so the object cannot be copied. 通过删除移动/复制ctors,它们无法被调用,因此无法复制对象。 Only one destructor can be called per constructor (barring manual construction or destruction). 每个构造函数只能调用一个析构函数(禁止手动构建或销毁)。

If the code tries to call them, it will generate an error at compile time. 如果代码试图调用它们,它将在编译时生成错误。


In C++17 you can even return A(a,b); 在C ++ 17中,你甚至可以return A(a,b); and a similar guarantee occurs. 并发生类似的保证。

You can also A a = make_A(1,2); 你也可以A a = make_A(1,2); and a similar guarantee occurs. 并发生类似的保证。

This is described as "guaranteed elision", but rather it turns some operations into "descriptions of how to construct something". 这被描述为“保证省略”,而是将一些操作变成“如何构建某些东西的描述”。

So there are cases where you'll be able to do things that required move or copy ctors in C++03 or C++11 or C++14 but in C++17 now do something similar to "elision" and no longer require the move or copy ctors. 因此,在某些情况下,您可以在C ++ 03或C ++ 11或C ++ 14中执行需要移动或复制ctors的操作,但在C ++ 17中,现在执行与“elision”类似的操作更长时间需要移动或复制ctors。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么带有支撑初始化列表的Constructor / virtual析构函数不起作用? - Why doesn't Constructor/virtual destructor with braced initializer list work? 支撑初始化列表中的对象创建顺序 - Object creation order in braced init list 为什么不能保证分配成本小于构造函数对析构函数对? - Why the cost of assignment is not guaranteed to be less than a constructor-destructor pair? 调用带有括号初始列表的显式构造函数:模棱两可? - Calling an explicit constructor with a braced-init list: ambiguous or not? 为什么在按值返回列表初始化对象时不调用copy-constructor? - Why is the copy-constructor not called when returning by-value a list-initialized object? 我可以使用braced-init-list作为std :: variant的向量吗? - Can I use braced-init-list for a vector of std::variant? 派生类对象 - Braced init - Derived class object - Braced init 对象创建中涉及的构造函数和析构函数调用 - constructor and destructor calls involved in object creation 使用继承时构造函数/析构函数调用的顺序 - Order of Constructor/Destructor Calls When Using Inheritance 用于 protobuf 转换的支撑初始化样式构造函数 - Braced-init style constructor for protobuf conversion
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM