使用Boost适配器与C++11 lambda表达式

21

我尝试编译这段代码:

#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <vector>

int main() {
    std::vector<int> v{
        1,5,4,2,8,5,3,7,9
    };
    std::cout << *boost::min_element(v | boost::adaptors::transformed(
            [](int i) { return -i; })) << std::endl;
    return 0;
}
编译失败,并显示以下错误消息(在长模板实例化小说之后):
/usr/local/include/boost/iterator/transform_iterator.hpp:84:26: error: use of deleted function ‘main()::<lambda(int)>::<lambda>()’
../main.cpp:12:5: error: a lambda closure type has a deleted default constructor

我在谷歌上搜到了这个问题,发现从Boost用户邮件列表归档中找到了这个。建议使用#define BOOST_RESULT_OF_USE_DECLTYPE来解决问题。我把它放在了我的代码非常开始的位置,但还是无法编译。错误消息的长度似乎短多了,但最后的错误消息仍然相同。我目前正在使用Boost 1.50。

这里可能出了什么问题?有没有办法让它工作?


1
我认为你需要1.51版本。至少这是让它对我起作用的版本。 - Gurgeh
5个回答

11

你可以在非捕获lambda函数前面加上 "+" 将其转换为函数指针。

std::vector<int> v{1,5,4,2,8,5,3,7,9};
std::cout << *boost::min_element(v | 
    boost::adaptors::transformed(+[](int i) 
    {
        return -i; 
    })) << std::endl;

10

http://smellegantcode.wordpress.com/2011/10/31/linq-to-c-or-something-much-better/

但是你可以使用这个,它很有效。
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <vector>
#include <functional>

int main() {
    std::vector<int> v{
        1,5,4,2,8,5,3,7,9
    };
    std::function<int(int)> func = [](int i) { return -i; };
    std::cout << *boost::min_element(v | boost::adaptors::transformed(
    func)) << std::endl;
    return 0;
}

http://liveworkspace.org/code/b78b3f7d05049515ac207e0c12054c70

#define BOOST_RESULT_OF_USE_DECLTYPE 在VS2012中可以正常工作。


std::function在类型擦除中起作用,即通过虚函数实现。这种额外的开销在这里是不必要的,可能也是不可接受的。(同时也会阻碍内联)我的解决方案是使用std::ref - undefined

6

这个问题在http://boost.2283326.n4.nabble.com/range-cannot-use-lambda-predicate-in-adaptor-with-certain-algorithms-td3560157.htmlhttps://svn.boost.org/trac/boost/ticket/4189上有讨论。问题在于,一些算法希望能够复制构造(和默认构造、复制赋值)它们的谓词,而这是使用lambda无法实现的。

解决方法是将lambda包装在一个std::function中:

*boost::min_element(
    v | boost::adaptors::transformed(std::function<int(int)>(
        [](int i) { return -i; })));

我曾在Inferring the call signature of a lambda or arbitrary callable for "make_function"上发帖询问如何编写make_function函数,以便只需编写以下代码:

*boost::min_element(
    v | boost::adaptors::transformed(make_function(
        [](int i) { return -i; })));

0

使用 C++17 的特性 class template argument deduction,您可以更简单地包装 std::function,如下所示:

*boost::min_element(
    v | boost::adaptors::transformed(std::function(
        [](int i) { return -i; })));

0
正如其他人已经提到的,问题在于std::min_element()(简单地由boost::min_element()包装)希望复制范围的开始迭代器。但是,这个迭代器包含一个转换可调用对象的实例(使用过滤可调用对象时也是同样的情况),因此必须将其复制。但是,lambda函数是不可复制的,所以这将无法编译。
有人建议将lambda函数包装在std::function中,但这会带来每次调用都会有一次虚函数调用的缺点。虚函数调用是不免费的,你将不得不为范围中的每个元素支付这个代价。(虚函数还会阻止内联)
更好的解决方案是使用std::cref(),这样迭代器将包含一个基本上是指针的std::reference_wrapper
static constexpr auto transformer = [](int i) { return -i; };
std::cout << *boost::min_element(v | boost::adaptors::transformed(std::cref(transformer))) << std::endl;

Coliru上看到它的工作。

这也适用于捕获lambda函数,但是这些函数不能是static constexpr


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接