在以下代码中:
#include <execution>
#include <vector>
template<typename T>
class Index {
public:
const std::string& text;
const std::vector<T>& index;
decltype(index.begin())& begin() { return index.begin(); } // No warning
decltype(index.end())& end() { return index.end(); } // No warning
const decltype(index.cbegin())& begin() const { return index.cbegin(); }
const decltype(index.cend())& end() const { return index.cend(); }
};
int main()
{
}
Visual Studio 2022(最新版本)给出警告:
每行警告 C4172:返回局部变量或临时变量的地址
两次
decltype
。同时具有相同配置的godbolt则不然。
这是误报警告还是应该修复代码?
我在代码中的意图是返回 const 迭代器的 const 引用。这是正确的方法/语法吗?
更新
有趣的是,对于非常量版本(刚刚添加到上面的源代码中),没有一个编译器给出警告,尽管我们仍然对本地副本有相同的引用。
更新2
我意识到实际上我确实需要这两位新成员,而我唯一需要的是:
template<typename T>
class Index {
public:
const std::string& text;
const std::vector<T>& index;
decltype(index.begin()) begin() const { return index.begin(); }
decltype(index.end()) end() const { return index.end(); }
};
这涵盖了客户端代码中的所有需求。所以,我很幸运地尝试将
const
作为带有引用的返回值类型;结果我收到了这个警告,并在该线程的所有参与者的帮助下理解了它。
这不是误报,如果您实例化类模板,您可以在 Compiler Explorer 上重现它:
template class Index<int>;
MSVC 向您提供已发布的警告,GCC 向您提供
<source>:10:72: warning: returning reference to temporary [-Wreturn-local-addr]
10 | const decltype(index.cbegin())& begin() const { return index.cbegin(); }
| ~~~~~~~~~~~~^~
问题是
index.cbegin()
按值返回迭代器,而不是按引用返回。
返回类型为 const std::vector::const_iterator&
,并且此 const&
绑定到临时 const_iterator
对象。
你说过你要通过
const&
返回迭代器,因为
迭代器大小足够大而产生影响时的性能考虑。
这不是您应该担心的事情。 迭代器是指针的泛化([iterator.requirements.general] p1),并且是小型、轻量级的对象,可以廉价地按值传递。 如果迭代器的传递成本很高,那就是迭代器的设计问题。
请注意,
<algorithm>
中的标准库算法也按值获取迭代器。
如果 P2748: Disallow Binding a Returned Glvalue to a Temporary 被 C++26 标准接受(并且总体共识是有利的),那么你的代码将是格式错误的。
换句话说,它甚至可能无法在 C++26 中编译。
您不是通过引用返回您所拥有的内容,而是返回函数调用的结果。
该函数恰好位于您引用的方法上是无关紧要的。
const std::vector<T>& index;
const decltype(index.cbegin())& begin() const { return index.cbegin(); }
解决方案是不返回引用——只返回副本。迭代器的复制成本应该很低。