我对有关复制省略的新规则感到有点困惑,实际上我甚至不确定它是否适用于这种情况。我有这个:
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
。
答案 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 ++标准。