[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.