【问题标题】:Lifetime of temporaries in initializer_lists nested in function calls嵌套在函数调用中的 initializer_lists 中的临时对象的生命周期
【发布时间】:2017-01-21 21:39:25
【问题描述】:

func({someString, std::string("foo")}) 这样的临时对象的生命周期是多少?我问是因为有问题的initializer_list 是 实际上类型为initializer_list<Foo>(不是initializer_list<string>),并且 Foo 的 ctor 将其参数的地址存储为指针。会不会这样 在创建 initializer_list 之后,但在创建之前,临时对象被销毁 函数调用?还是临时工会一直活到 函数调用?

下面提供了一些示例代码,以及运行产生的输出 clang++ 8.0.0 的代码。临时工似乎在这段时间内活着 我的特定系统上的函数调用,但是这种行为标准, 并且在不同的编译器之间会保持一致吗?

#include <stdio.h>
#include <vector>
#include <string>
#include <initializer_list>

using namespace std;

class Foo
{
public:
    const string* x;

    Foo(const string& str) : x(&str) {
        printf("Foo::Foo()\n");
    }

    ~Foo() {
        printf("Foo::~Foo()\n");
    }
};

void
funcA(initializer_list<Foo> args)
{
    (void)args;
    printf("funcA()\n");
}

void
funcB(vector<Foo> args)
{
    (void)args;
    printf("funcB()\n");
}

void
funcC(const vector<Foo>& args)
{
    (void)args;
    printf("funcC()\n");
}

int
main()
{
    string a;

    printf("=========\n");
    funcA({a, string("foo")});
    printf("=========\n");
    funcB({a, string("bar")});
    printf("=========\n");
    funcC({a, string("baz")});
    printf("=========\n");

    return 0;
}

示例输出:

=========
Foo::Foo()
Foo::Foo()
funcA()
Foo::~Foo()
Foo::~Foo()
=========
Foo::Foo()
Foo::Foo()
funcB()
Foo::~Foo()
Foo::~Foo()
Foo::~Foo()
Foo::~Foo()
=========
Foo::Foo()
Foo::Foo()
funcC()
Foo::~Foo()
Foo::~Foo()
Foo::~Foo()
Foo::~Foo()
=========

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    临时对象的生命周期直到语句结束。它们将可用于此语句中的任何函数调用,但不能超出。因此,您可以传递引用并使用临时对象。他们的地址不应该被存储起来以供以后参考。

    【讨论】:

    • 强调最后一段。如果函数将参数存储在对象中以供以后使用,则任何后续使用引用(或在这种情况下取​​消引用指针)都会产生未定义的行为。
    • “语句结束”不太正确。例如,if (x == f()) { /* ... */ },由f() 产生的临时对象在if 语句的复合语句之前被销毁。 Demo.
    • @Peter 指针仅在函数调用本身中使用。我这样做的原因是,当通过花括号 initializer_list 表示法将对象传递给函数时,无法避免复制。你不能有 std::vector,并且 std::vector 禁止使用像 func({T(), T(), ...}) 这样的临时变量。这个奇怪的黑客是唯一的解决方法。你传递一个临时的,然后存储一个指向它的指针......而且,该死的,我刚刚意识到移动语义消除了所有这些问题。 FML。
    • 虽然实际上,即使我在这里依靠移动语义来有效地在诸如 func({T(), T(), ...}) 之类的调用中传递临时变量,那么仍然会有复制涉及诸如 func({existingValueA, existingValueB, ...}) 之类的内容。为此,我认为您仍然需要我的技巧。这不是这篇文章的重点,但如果有人有其他想法,请告诉!
    猜你喜欢
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    • 2023-02-07
    • 1970-01-01
    • 2013-11-20
    • 2013-06-18
    • 2016-12-25
    • 2019-01-06
    相关资源
    最近更新 更多