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

destructor called before temporary should be out of scope

我有一些代码在 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;
}

在 Visual Studio 下,由 zero_params 编辑的字符串 return 被推断为右值引用。此外,该对象的析构函数在 test1() 内部被调用,其中 return 从调用 f 发生(这似乎是一个合理的地方来破坏 && 对象)。

在 GCC 下,字符串 returned 不会被推断为右值引用。如我所料,在 cout 语句中使用后调用析构函数。

将 return 类型指定为 'string' 而不是 Visual Studio 下的 decltype(auto) 修复它,就像在 return 上使用 remove_reference_t 一样test1.

中的 f()

我的期望是 GCC 是正确的,因为 zero_params() 的函数签名是字符串,而不是字符串&& 所以我希望 'bubble up' 不会引用 return test1 的类型,如果它使用 decltype(auto)。

这是正确的评估吗?


后期编辑:

我发现使用 VS2015 解决此问题的另一种方法是将提供给 test1 的函数包装在 lambda 中:

cout << test1(zero_params) << endl;

至:

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

所以based on the comments我们可以得出结论:

错误在于:

  • 编译器应该推断出 return 类型为字符串
  • 居然推导出是字符串&&
  • 因此它过早地破坏了价值

解决方法是:

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