Lambda适用于最新的Visual Studio,但在其他地方不起作用

Jon*_*Mee 8 c++ lambda visual-studio compiler-bug visual-studio-2015

所以我写了一个讨厌的lambda来满足"实现这个问题所需的最短代码量"问题:

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));
    })
));
Run Code Online (Sandbox Code Playgroud)

我的问题是在Visual Studio社区2015 Update 3版本14.0.25425.01上输出所需的:

4.2 9.1 2.3 0.6 6.4 3.6 1.4 7.5

但是在我试过的所有其他编译器上我得到了:

4.2 2.3 0.6 1.2 0.3 1.4 2.5 7.5

谁能告诉我是什么导致了不同的行为?

Tar*_*ama 10

您依赖的事实是,您传递给算法的确切闭包是用作谓词的闭包,但标准允许复制它:

[algorithms.general]/10 (N4140):[注意:除非另有说明,否则允许将函数对象作为参数的算法自由复制这些函数对象.对象标识很重要的程序员应考虑使用指向非复制实现对象(如reference_wrapper(20.9.3))或某种等效解决方案的包装类. - 尾注]

这正是libstdc ++所做的.从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;
}
Run Code Online (Sandbox Code Playgroud)

这调用std::__find_if的函数复制的开始__pred,这意味着价值i递增内的一位std::__find_if,但是这并不能改变什么事情在调用点.

要解决此问题,您可以使用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))));
Run Code Online (Sandbox Code Playgroud)

现场演示

  • @JonathanMee大多数算法的签名按值计算其函子,至少根据cppreference. (2认同)
  • @JonathanMee我的观点更多的是由于按值参数而至少被复制一次,如果用其他算法实现则更多次 (2认同)