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

qea*_*adz 13 c++ decltype auto c++14 visual-studio-2015

我有一些代码在VS2015下失败,但在GCC下工作.我很确定这个bug是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;
}
Run Code Online (Sandbox Code Playgroud)

在Visual Studio下,zero_params返回的字符串被推导为右值引用.此外,该对象的析构函数在test1()中调用,其中从f调用返回f(这似乎是破坏&&对象的合理位置).

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

在Visual Studio下将返回类型指定为'string'而不是decltype(auto)可以修复它,就像在test1中返回f()时使用remove_reference_t一样.

我的期望是GCC是正确的,因为zero_params()的函数签名是字符串,而不是字符串&&所以如果它使用decltype(auto),我会期望非引用'冒泡'到test1的返回类型.

这是正确的评估吗?


晚编辑:

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

cout << test1(zero_params) << endl;
Run Code Online (Sandbox Code Playgroud)

至:

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

Mob*_*isk 2

所以根据评论我们可以得出结论:

错误在于:

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

解决方法是:

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