我有一个返回 lambda 的函数。
auto make_scanner(std::vector<int> const &v) {
auto begin = v.cbegin();
auto end = v.cend();
return [begin, end] () mutable -> int {
return (begin != end) ? *begin++ : 0;
};
}
请注意,lambda 将迭代器捕获到向量中,而不是向量本身。 在常见的用例中,这很好:
// Common Use Case
std::vector<int> data = {1, 2, 3};
auto scanner = make_scanner(data);
for (int x = scanner(); x != 0; x = scanner()) {
// compute something
}
但是如果调用者从一个未命名的临时向量创建一个扫描器,那么 lambda 就会留下悬空的迭代器:
// Incorrect Use Case
auto scanner = make_scanner(std::vector<int>{1, 2, 3});
// Invoking scanner() is UB because the iterators reference
// the vector after its lifetime.
在非优化构建(即调试构建)中,即使代码错误,通常也会按预期工作。 在优化的构建中,如果幸运的话,它会崩溃或至少导致测试失败。
当使用未命名的临时向量调用 make_scanner 时,是否有办法使编译失败,同时仍然允许常见用例?
我一直在尝试使用右值引用进行各种重载,但我找不到答案。
(是的,我意识到可以使用非临时向量,但最终仍然会在 lambda 中得到悬空迭代器。我目前不关心这些情况。)
利用已删除的函数可以很容易地实现这一点。 我们需要知道的第一件事是,当重载函数时,当使用右值调用函数时,参数
T&&
优先于带有参数 const T&
的重载。
如果您随后删除右值引用重载,则重载决策将选择该函数作为右值,并且您将因使用已删除的函数而收到编译器错误。
对于你的代码,变成
auto make_scanner(std::vector<int> const &v) {
auto begin = v.cbegin();
auto end = v.cend();
return [begin, end] () mutable -> int {
return (begin != end) ? *begin++ : 0;
};
}
auto make_scanner(std::vector<int> &&v) = delete;
您可以在这个现场示例中看到它的工作原理。