在临时之前调用的析构函数应该超出范围

destructor called before temporary should be out of scope

本文关键字:范围 析构函数 调用      更新时间:2023-10-16

我有一段代码在VS2015下失败,但在GCC下可以工作。我很确定这个错误是Visual Studio造成的,但我想确保我对decltype(auto)的理解是正确的。

#include <iostream>
using namespace std;
string zero_params()
{
  return "zero_params called.";
}
template< typename F >
auto test1( F f ) -> decltype(auto)
{
  return f();
}
int main() {
  cout << std::is_rvalue_reference< decltype(test1(zero_params)) >::value << endl;
  cout << test1(zero_params) << endl;
  cout << "Done!" << endl;
  return 0;
}

在VisualStudio中,zero_params返回的字符串被推断为右值引用。此外,该对象的析构函数在test1()中被调用,调用f的返回发生在这里(这似乎是析构函数a&&对象的合理位置)。

在GCC下,返回的字符串不会被推断为右值引用。正如我所期望的那样,析构函数在cout语句中使用后被调用。

在Visual Studio中将返回类型指定为"string"而不是decltype(auto)可以修复此问题,在test1中对f()的返回使用remove_reference_t也是如此。

我的期望是GCC是正确的,因为zero_params()的函数签名是字符串,而不是字符串&amp;因此,如果它使用decltype(auto),我希望非引用"冒泡"到test1的返回类型。

这是一个正确的评估吗?


后期编辑:

我发现用VS2015解决这个问题的另一种方法是将给定给test1的函数封装在lambda:中

cout << test1(zero_params) << endl;

至:

cout << test1( [](auto&&... ps) { return zero_params(std::forward<decltype(ps)>(ps)...); } ) << endl;

因此,根据评论,我们可以得出结论:

  • 这是VS2015预览中的一个错误
  • 有变通办法
  • 已报告错误

错误在于:

  • 编译器应该将返回类型推导为字符串
  • 它实际上推断它是字符串&amp
  • 因此,它过早地破坏了价值

解决方法有:

  • 不要对函数的返回类型使用decltype(auto)
  • 在传入函数之前,将其包装在lambda表达式中