[stmt.ranged] in the current working draft provides definition of the range based for that is easy to misuse:
#include <iostream>
#include <vector>
struct test {
std::vector<int> val = {1, 2, 3};
std::vector<int>& get() { return val; }
~test() {
std::cerr << "~test() \n";
}
};
int main() {
for (auto&& v: test().get()) {
std::cerr << v << ' ';
}
}
In the above example call to the destructor for temporary test variable happens before the iteration over the returned value. This leads to the following output:
~test() 0 0 3
This paper proposes a solution for the above problem and for two core issues CWG 900 and CWG 1498.
Modify the [stmt.ranged] paragraph 1 to keep the for-range-initializer expression for the whole execution time of the range:
The range-based for statement:
for (init-statementopt for-range-declaration : for-range-initializer ) statement
is equivalent to
{
init-statementopt
[&](auto &&__range) {
auto &&__range = for-range-initializer ;
auto __begin = begin-expr ;
auto __end = end-expr ;
for ( ; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}(for-range-initializer);
}
This change makes sure that for-range-initializer lives long enough to finish the iteration:
#include <iostream>
#include <vector>
struct test {
std::vector<int> val = {1, 2, 3};
std::vector<int>& get() { return val; }
~test() {
std::cerr << "~test() \n";
}
};
int main() {
[&](auto&& __range) {
auto __begin = std::begin(__range) ;
auto __end = std::end(__range) ;
for ( ; __begin != __end; ++__begin ) {
auto&& v = *__begin;
std::cerr << v << ' ';
}
}(test().get());
}
Code from above outputs:
1 2 3 ~test()
For the purposes of SG10, we recommend the feature-testing macro name __cpp_safe_range_based_for.