返回的值是否会被临时删除?

时间:2018-03-20 08:32:37

标签: c++ templates destructor

我对有关复制省略的新规则感到有点困惑,实际上我甚至不确定它是否适用于这种情况。我有这个:

template <typename T> struct foo {
    T t;
    foo(const T& t) : t(t) {}
    ~foo() { std::cout << "destructor \n"; }
}

template <typename T> foo<T> make_foo(const T& t) { return {t}; }

其中make_foo只是为了允许推导出参数(在实际代码t中是一个lambda,但为了简单起见我把它留在这里,或者更确切地说是为了混淆,对不起,如

auto x = make_foo(123);

现在我需要绝对确定foo的析构函数只被调用一次:当x超出范围时。我担心这是一个不清楚 - 你在问什么问题,但如果显而易见的话,那就不会有任何临时的foo,那就足够了;)。

在C ++ 11中,我可以确定foo中的临时make_foo是否会被销毁?只有在x超出范围时才应调用析构函数。

正如评论中正确指出的那样,这个问题是XY问题的Y部分,X部分是我想实现范围功能的一部分。 foo的析构函数有一些副作用(在示例中为cout),应该在x范围的末尾调用,但不会在make_foo中调用以防万一暂时foo

3 个答案:

答案 0 :(得分:2)

在C ++ 11中,即使对于无名的临时复制,也只允许没有强制要求。这里描述copy elision。它是从C ++ 17开始强制执行的。

同样在C ++ 17中,您将拥有自动扣除指南,因此您不需要这样的结构。

你可以测试你的编译器,因为大多数现代编译器都会在这里忽略复制。

答案 1 :(得分:1)

从C ++ 17开始,保证不会有临时性。

在C ++ 14及更早版本中,必须有一个可访问的复制/移动构造函数,无论是否存在临时构造函数,编译器都是可选的。

据我所知,实际显示临时版本的唯一编译器是调试模式下的旧版MSVC。

答案 2 :(得分:1)

在您的情况下,为了确保不会为未命名对象调用析构函数,您可以将返回值绑定到 const引用。 澄清如果我们不依赖复制省略会发生什么:

template <typename T> foo<T> make_foo(const T& t) { return {t}; }

在此函数中,不会在该函数的范围内构造返回对象。它将在范围外创建临时未命名对象。 如果要将返回值绑定到新命名对象,将调用移动构造函数(如果未定义移动,则复制),以便从返回临时创建新对象 即可。但是,如果将返回的临时绑定到 const引用,它将严格绑定到该引用,并且不会构造任何新对象,并且在该引用超出范围之前不会破坏临时。

编辑: 不要误导你。临时调用函数作用域的构造函数,但该临时函数的生命周期确实会延长到const引用的生命周期

如果您需要更多信息,可以查看此答案。它指的是C ++标准。

Does a const reference prolong the life of a temporary?