在使用Modern C++中的示例时,我编写了以下代码.
#include <string>
#include <iostream>
static int count = 0;
class Counter
{
public:
Counter() { ++count; };
Counter(Counter& r) { ++count; };
Counter(Counter&& r) { ++count; };
~Counter() { --count; };
void foo() {};
};
decltype(auto) foo_warn()
{
Counter c;
return (c); // Warning about returning local reference
}
decltype(auto) foo_no_warn()
{
Counter c;
return 1==1 ? c : c; // No warning, still local reference returned
}
int main()
{
Counter& a = foo_warn();
Counter& b = foo_no_warn();
std::cout << count << std::endl; // prints 0
a.foo();
b.foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用命令编译的代码:
g++-6 -std=c++14 -Wall -O0 decl_fail.cpp -o decl_fail
输出:
g++-6 -std=c++14 -Wall -O0 decl_fail.cpp -o decl_fail
decl_fail.cpp: In function ‘decltype(auto) foo_warn()’:
decl_fail.cpp:19:10: warning: reference to local variable ‘a’ returned [-Wreturn-local-addr]
Counter a;
^
Run Code Online (Sandbox Code Playgroud)
很显然,对我说,decltype(auto)返回的表达式(但仍然不够直观)的参考,因此a和b无效引用(通过验证count==0).
问题是为什么编译器没有警告我这个foo_no_warn?
我刚刚在编译器中发现了一个错误,或者这是一些可解释的行为?
首先我们要声明这个问题与 decltype(auto) 没有显式相关,因为如果函数显式返回 Counter& ,您将得到几乎相同的结果。
您可以考虑以下代码:
typedef std::vector<int> Type;
class DataContainer {
public:
DataContainer() : data(Type(1024, 0)) {}
const Type& getData() const { return data; }
private:
const Type data;
};
const Type& returnLocalRef()
{
DataContainer container;
const Type& data = container.getData();
return data; // o! returning a ref to local - no warning for most compilers
}
Run Code Online (Sandbox Code Playgroud)
尽管返回了本地引用,但编译器在 VS2015 和 gcc48(使用 -Wall)中都不会发出警告。但是,如果您从const Type& data中删除引用,编译器会立即捕获问题。您应该将这种行为视为错误吗?有争议的。
编译器的基本工作是编译代码。它警告开发人员一些明显的问题,但在大多数情况下,它不会对程序逻辑进行任何更深入的分析(编译时间会受到影响)。这就是代码静态分析工具的开发和用途。
所描述的情况可以被认为是一个简单的示例,但是单层间接就足以“欺骗”编译器。为了验证这一点,编译器需要检查 getData 方法实际返回的内容。
进行简单修改:
Type globalData;
...
const Type& getData() const { return globalData; }
Run Code Online (Sandbox Code Playgroud)
将使 returnLocalRef 函数返回的引用有效。因此,这可以被认为是编译器在分析复杂性和时间效率之间的权衡。
| 归档时间: |
|
| 查看次数: |
301 次 |
| 最近记录: |