// g++ --std=c++17 test.cpp -I /usr/local/include -L /usr/local/lib -lboost_system -Wall -pedantic -Wreturn-type -Wstrict-aliasing -Wreturn-local-addr -fsanitize=address -g
// LD_LIBRARY_PATH=/usr/local/lib ./a.out
#include <iostream>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
class A {
public:
fs::path path_;
const fs::path & path() const { return path_; }
fs::path & path() { return path_; }
};
class B {
public:
fs::path root_path_;
A path_2;
A path_3;
const fs::path & operator()() const {
for (const auto & path : {
path_3.path(),
path_2.path(),
root_path_
}) {
if (not path.empty()) {
return path;
}
}
throw std::logic_error{"using default-constructed B"};
}
};
int main(int argc, char **argv) {
B b;
b.root_path_ = "foo/";
b.path_2.path() = "foo/bar";
b.path_3.path() = "foo/baz";
std::cout << b() << '\n';
return 0;
}
上面的代码,尽我所知,似乎是有效的C++。相反,当调用时,我得到垃圾输出。范围为变量的循环导致返回局部变量的地址引用?
g++
最初没有抱怨,但地址Sanitizer 确实。 g++
终于抱怨加入-O2
。生成的警告是
test.cpp: In member function ‘const boost::filesystem::path& B::operator()() const’:
test.cpp:31:12: warning: function may return address of local variable [-Wreturn-local-addr]
return path;
^~~~
test.cpp:29:3: note: declared here
}) {
^
请注意,我用的:
$ cat /etc/fedora-release
Fedora release 25 (Twenty Five)
$ g++ --version
g++ (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
请注意,我用一个指针,而不是解决了错误。
const fs::path & operator()() const {
for (const auto * path : {
&path_3.path(),
&path_2.path(),
&root_path_
}) {
if (not path->empty()) {
return *path;
}
}
throw std::logic_error{"using default-constructed B"};
}
但是,这并留在我脑海中的一些问题:
- 为什么
g++
不抱怨的问题,直到-O2
加入? - 究竟是什么关于我的代码是未定义?我会说这是明确的:
B::operator() const
是......好的......常量。这应该表示中使用的对象不是本地人就是const成员。它访问const成员。它构造了一个局部变量const auto &
,它应该引用一个const成员字段。究竟是什么导致它绑定到临时?
为什么参考首先被返回? – chris
这个例子减少了;我使用'operator()'作为选择器函数来决定将哪个成员变量用作另一个函数的输入。当我不需要修改它时,为什么要返回一个非const(或特别是非const的引用)? – inetknght
通过'const'值返回有普遍的缺点。首先,调用者的返回值是孤立的,所以对它的修改无关紧要。其次,它抑制RVO。无论如何,如果您在实际代码中使用初始化程序列表,那么结果并不会很好,因为这些程序需要复制,除非语言最终更改为允许移动。 – chris