考虑这个例子:
#include <vector>
#include <string>
#include <functional>
#include <iostream>
using closure_type = std::function<void(void)>;
using closure_vec = std::vector<closure_type>;
class callbacks {
static closure_type common(std::string name, uint32_t number) {
return [number, name]() { std::cout << name << number << std::endl; };
}
public:
static closure_type foo(uint32_t number) { return common("foo ", number); }
static closure_type print(std::string msg) {
return [msg]() { std::cout << "print " << msg << std::endl; };
}
};
template <typename... calls_t> closure_vec wrap(uint32_t number, calls_t &&... calls) {
return closure_vec {
callbacks::foo(number),
std::forward<calls_t>(calls)...,
};
}
int main() {
auto vec = wrap(42,
callbacks::print("hello, "),
callbacks::print("world"));
for(auto &e: vec)
e();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Demo(最右边的选项卡上有完整的消息)
当使用 clang-tidy 检查此代码时,我收到以下警告:
warning: Potential memory leak [clang-analyzer-cplusplus.NewDeleteLeaks]
Run Code Online (Sandbox Code Playgroud)
行号指向函数的作用域出口wrap。
据我了解该消息,该工具担心结果表格callbacks::foo可能会丢失。但我不明白这怎么可能:std::function是一个安全的类,应该在其析构函数中很好地销毁所有内容。而且它的寿命是由向量控制的,这也是安全的。
这里发生了什么?我该如何解决这个问题或解决方法?
不幸的是,我不能仅仅抑制警告,因为该代码分散在代码库中的各处。
尝试
closure_vec retval;
retval.reserve(sizeof...(calls)+1);
retval.push_back(callbacks::foo(number));
( retval.push_back(std::forward<calls_t>(calls)), ... );
return retval;
Run Code Online (Sandbox Code Playgroud)
这避免了 constinitializer_list 包含您的代码创建的 std 函数的副本,因此也应该更高效。
我在这里尝试使用 C 样式数组,但尽管没有使用 std::initializer_list,但我也收到了警告。
这也有效:
std::array<closure_type, sizeof...(calls)+1> tmp ={
nullptr,
std::forward<calls_t>(calls)...
};
tmp[0] = callbacks::foo(number);
return {std::make_move_iterator(std::begin(tmp)), std::make_move_iterator(std::end(tmp))};
Run Code Online (Sandbox Code Playgroud)
问题callbacks::foo(number)出在初始化过程中。