Lambda runs on the latest Visual Studio but doesn't work elsewhere

So, I wrote an unpleasant lambda to satisfy the "shortest amount of code needed to achieve this" question :

values.resize(distance( begin(values), remove_if(begin(values), end(values), [i = 0U, it = cbegin(intervals), end = cend(intervals)](const auto&) mutable { return it != end && ++i > it->first && (i <= it->second || (++it, true)); }) )); 

My problem is that in Visual Studio Community 2015 Update 3 version 14.0.25425.01 the desired is output:

4.2 9.1 2.3 0.6 6.4 3.6 1.4 7.5

But to all the other compilers that I tried, I get:

4.2 2.3 0.6 1.2 0.3 1.4 2.5 7.5

Can someone tell me what causes different behavior?

+8
c ++ lambda visual-studio visual-studio-2015 compiler-bug
source share
1 answer

You rely on the exact closure that you pass into the algorithm to be used as a predicate, but the standard allows you to copy it:

[algorithms.general]/10 (N4140) : [Note: unless otherwise specified, algorithms that take function objects as arguments can freely copy these functional objects. Programmers who care about the identity of an object should consider using a wrapper class that points to an object that cannot be processed, such as reference_wrapper (20.9.3), or some equivalent solution. -end note]

This is exactly what libstdc ++ does. From v6.2.1:

 template<typename _ForwardIterator, typename _Predicate> _ForwardIterator __remove_if(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { __first = std::__find_if(__first, __last, __pred); if (__first == __last) return __first; _ForwardIterator __result = __first; ++__first; for (; __first != __last; ++__first) if (!__pred(__first)) { *__result = _GLIBCXX_MOVE(*__first); ++__result; } return __result; } 

This is a call to std::__find_if at the beginning of the copy __pred function, which means that the value of i increases within std::__find_if , but this does not change what happens when the site is called.

To fix this problem, you can use std::ref :

 auto clos = [i = 0U, it = cbegin(intervals), end = cend(intervals)](const auto&) mutable { return it != end && ++i > it->first && (i <= it->second || (++it, true)); }; values.resize(distance(begin(values), std::remove_if(begin(values), end(values), std::ref(clos)))); 

Live demo

+10
source share

All Articles