我有一个返回 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 时,是否有办法导致编译失败,同时仍允许常见用例?
我一直在尝试使用 r 值引用进行各种重载,但答案却让我困惑。
(是的,我意识到可以使用非临时向量,但最终在 lambda 中仍然会出现悬垂迭代器。我现在不关心这些情况。)
利用已删除的函数很容易实现这一点。我们需要知道的第一件事是,当使用右值调用函数时,重载函数时
T&&
参数优先于const T&
使用参数的重载。如果您随后删除右值引用重载,则重载解析将为右值选择该函数,并且您将因使用已删除的函数而收到编译器错误。
对于您的代码,
你可以在实例中看到它的工作原理。
您可以提供一个右值删除的重载。
您还可以为右值向量创建一个工作过载,它必须在 lambda 内移动向量:
这是一个演示。