【问题标题】:Boost algorithm with lambda fails to compile带有 lambda 的 Boost 算法无法编译
【发布时间】:2017-11-18 00:12:32
【问题描述】:

以下示例代码:

#define BOOST_RESULT_OF_USE_DECLTYPE // does not help

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

struct DW {
    std::promise<bool> success_;
};

template <class T>
class Pool
{
public:
    Pool() : container_{}
    {};
public:
    typename std::vector<T>::iterator begin() { return container_.begin(); }
    typename std::vector<T>::iterator end()   { return container_.end(); }
    typename std::vector<T>::const_iterator begin() const { return container_.begin(); }
    typename std::vector<T>::const_iterator end()   const { return container_.end(); }
private:
    std::vector<T>     container_;     ///< holder of elements
};

#define LAMBDA [] (DW& w) { return w.success_.get_future().get(); }

int main()
{
    Pool<DW> vec;

    auto s = std::count_if(vec.begin(), vec.end(), LAMBDA);
    auto b = boost::count_if(vec, LAMBDA);

    return (s == b) ? 0 : 1;
}

使用 GCC 4.8.5、6.2 或 8 编译失败:

g++ -g -pthread -std=gnu++0x -Wall -pedantic -Wextra -Wformat=2 -o "ranger" "ranger.cpp" (in directory: /tmp)
In file included from /usr/local/include/boost/iterator/iterator_categories.hpp:15:0,
                 from /usr/local/include/boost/iterator/iterator_concepts.hpp:10,
                 from /usr/local/include/boost/range/concepts.hpp:20,
                 from /usr/local/include/boost/range/algorithm.hpp:29,
                 from ranger.cpp:6:
/usr/local/include/boost/mpl/eval_if.hpp: In instantiation of ‘struct boost::mpl::eval_if_c<false, boost::range_const_iterator<Pool<DW>, void>, boost::range_mutable_iterator<Pool<DW>, void> >’:
/usr/local/include/boost/range/iterator.hpp:69:17:   required from ‘struct boost::range_iterator<Pool<DW>, void>’
/usr/local/include/boost/range/difference_type.hpp:26:12:   required from ‘struct boost::range_difference<Pool<DW> >’
/usr/local/include/boost/range/algorithm/count_if.hpp:32:1:   required by substitution of ‘template<class SinglePassRange, class UnaryPredicate> typename boost::range_difference<T>::type boost::range::count_if(SinglePassRange&, UnaryPredicate) [with SinglePassRange = Pool<DW>; UnaryPredicate = main()::__lambda5]’
ranger.cpp:34:41:   required from here
/usr/local/include/boost/mpl/eval_if.hpp:60:31: error: no type named ‘type’ in ‘boost::mpl::eval_if_c<false, boost::range_const_iterator<Pool<DW>, void>, boost::range_mutable_iterator<Pool<DW>, void> >::f_ {aka struct boost::range_mutable_iterator<Pool<DW>, void>}’
     typedef typename f_::type type;
                               ^
/usr/local/include/boost/mpl/eval_if.hpp: In instantiation of ‘struct boost::mpl::eval_if_c<true, boost::range_const_iterator<Pool<DW>, void>, boost::range_mutable_iterator<const Pool<DW>, void> >’:
/usr/local/include/boost/range/iterator.hpp:69:17:   required from ‘struct boost::range_iterator<const Pool<DW>, void>’
/usr/local/include/boost/range/difference_type.hpp:26:12:   required from ‘struct boost::range_difference<const Pool<DW> >’
/usr/local/include/boost/range/algorithm/count_if.hpp:41:1:   required by substitution of ‘template<class SinglePassRange, class UnaryPredicate> typename boost::range_difference<const SinglePassRange>::type boost::range::count_if(const SinglePassRange&, UnaryPredicate) [with SinglePassRange = Pool<DW>; UnaryPredicate = main()::__lambda5]’
ranger.cpp:34:41:   required from here
/usr/local/include/boost/mpl/eval_if.hpp:60:31: error: no type named ‘type’ in ‘boost::mpl::eval_if_c<true, boost::range_const_iterator<Pool<DW>, void>, boost::range_mutable_iterator<const Pool<DW>, void> >::f_ {aka struct boost::range_const_iterator<Pool<DW>, void>}’
ranger.cpp: In function ‘int main()’:
ranger.cpp:34:41: error: no matching function for call to ‘count_if(Pool<DW>&, main()::__lambda5)’
     auto b = boost::count_if(vec, LAMBDA);
                                         ^
ranger.cpp:34:41: note: candidates are:
In file included from /usr/local/include/boost/range/algorithm.hpp:40:0,
                 from ranger.cpp:6:
/usr/local/include/boost/range/algorithm/count_if.hpp:41:1: note: template<class SinglePassRange, class UnaryPredicate> typename boost::range_difference<const SinglePassRange>::type boost::range::count_if(const SinglePassRange&, UnaryPredicate)
 count_if(const SinglePassRange& rng, UnaryPredicate pred)
 ^
/usr/local/include/boost/range/algorithm/count_if.hpp:41:1: note:   substitution of deduced template arguments resulted in errors seen above
/usr/local/include/boost/range/algorithm/count_if.hpp:32:1: note: template<class SinglePassRange, class UnaryPredicate> typename boost::range_difference<T>::type boost::range::count_if(SinglePassRange&, UnaryPredicate)
 count_if(SinglePassRange& rng, UnaryPredicate pred)
 ^
/usr/local/include/boost/range/algorithm/count_if.hpp:32:1: note:   substitution of deduced template arguments resulted in errors seen above
Compilation failed.

还有 clang 5.0.0 (trunk 302784)

$ clang++ -g -pthread -std=c++14 -Wall -pedantic -Wextra -Wformat=2  -o "ranger" "ranger.cpp" (in directory: /tmp)
In file included from ranger.cpp:6:
In file included from /usr/local/include/boost/range/algorithm.hpp:29:
In file included from /usr/local/include/boost/range/concepts.hpp:20:
In file included from /usr/local/include/boost/iterator/iterator_concepts.hpp:10:
In file included from /usr/local/include/boost/iterator/iterator_categories.hpp:15:
/usr/local/include/boost/mpl/eval_if.hpp:60:26: error: no type named 'type' in 'boost::range_mutable_iterator<Pool<DW>, void>'
    typedef typename f_::type type;
            ~~~~~~~~~~~~~^~~~
/usr/local/include/boost/range/iterator.hpp:65:31: note: in instantiation of template class 'boost::mpl::eval_if_c<false, boost::range_const_iterator<Pool<DW>, void>, boost::range_mutable_iterator<Pool<DW>, void> >' requested here
        typedef typename mpl::eval_if_c<
                              ^
/usr/local/include/boost/range/difference_type.hpp:28:40: note: in instantiation of template class 'boost::range_iterator<Pool<DW>, void>' requested here
                BOOST_DEDUCED_TYPENAME range_iterator<
                                       ^
/usr/local/include/boost/range/algorithm/count_if.hpp:31:38: note: in instantiation of template class 'boost::range_difference<Pool<DW> >' requested here
inline BOOST_DEDUCED_TYPENAME boost::range_difference<SinglePassRange>::type
                                     ^
ranger.cpp:34:14: note: while substituting deduced template arguments into function template 'count_if' [with SinglePassRange = Pool<DW>, UnaryPredicate = (lambda at ranger.cpp:34:35)]
    auto b = boost::count_if(vec, LAMBDA);
             ^
In file included from ranger.cpp:6:
In file included from /usr/local/include/boost/range/algorithm.hpp:29:
In file included from /usr/local/include/boost/range/concepts.hpp:20:
In file included from /usr/local/include/boost/iterator/iterator_concepts.hpp:10:
In file included from /usr/local/include/boost/iterator/iterator_categories.hpp:15:
/usr/local/include/boost/mpl/eval_if.hpp:60:26: error: no type named 'type' in 'boost::range_const_iterator<Pool<DW>, void>'
    typedef typename f_::type type;
            ~~~~~~~~~~~~~^~~~
/usr/local/include/boost/range/iterator.hpp:65:31: note: in instantiation of template class 'boost::mpl::eval_if_c<true, boost::range_const_iterator<Pool<DW>, void>, boost::range_mutable_iterator<const Pool<DW>, void> >' requested here
        typedef typename mpl::eval_if_c<
                              ^
/usr/local/include/boost/range/difference_type.hpp:28:40: note: in instantiation of template class 'boost::range_iterator<const Pool<DW>, void>' requested here
                BOOST_DEDUCED_TYPENAME range_iterator<
                                       ^
/usr/local/include/boost/range/algorithm/count_if.hpp:40:38: note: in instantiation of template class 'boost::range_difference<const Pool<DW> >' requested here
inline BOOST_DEDUCED_TYPENAME boost::range_difference<const SinglePassRange>::type
                                     ^
ranger.cpp:34:14: note: while substituting deduced template arguments into function template 'count_if' [with SinglePassRange = Pool<DW>, UnaryPredicate = (lambda at ranger.cpp:34:35)]
    auto b = boost::count_if(vec, LAMBDA);
             ^
ranger.cpp:34:14: error: no matching function for call to 'count_if'
    auto b = boost::count_if(vec, LAMBDA);
             ^~~~~~~~~~~~~~~
/usr/local/include/boost/range/algorithm/count_if.hpp:32:1: note: candidate template ignored: substitution failure [with SinglePassRange = Pool<DW>, UnaryPredicate = (lambda at ranger.cpp:34:35)]
count_if(SinglePassRange& rng, UnaryPredicate pred)
^
/usr/local/include/boost/range/algorithm/count_if.hpp:41:1: note: candidate template ignored: substitution failure [with SinglePassRange = Pool<DW>, UnaryPredicate = (lambda at ranger.cpp:34:35)]
count_if(const SinglePassRange& rng, UnaryPredicate pred)
^
3 errors generated.
Compilation failed.

boost::algo(range) 不应该是 std::algo(range.begin(), range.end()) 的替代品吗?

【问题讨论】:

  • 那么,它适用于普通的旧 vector&lt;DW&gt; 吗? vector&lt;int&gt; 和一个简单的 lambda [](int x){return x%2;} 怎么样? Pool&lt;int&gt;?不是由宏生成的 lambda?我想知道您需要什么相对复杂的设置。
  • 看起来您在Pool 中缺少某些类型定义,例如difference_type
  • @NathanOliver 我想知道这个;但是如果 boost 不烂,它应该能够从decltype( std::declval&lt;Pool&gt;().begin() )iterator_traits 中推断出这些东西,不是吗?我不熟悉这个特定的 boost API,但我尽量假设最好的 API。
  • @Yakk 我希望它会,但它看起来不像。将 std::vector&lt;DW&gt; foo; 添加到 main 然后执行 auto b = boost::count_if(foo, LAMBDA); 效果很好,所以我认为他们想要容器中的特征而不是迭代器。
  • 是的,它确实适用于普通的旧 vector&lt;DW&gt;。 lambda 是一个宏,表明它是相同的(我不想将它从 std 算法复制粘贴到 boost 算法)。

标签: c++ c++11 boost lambda


【解决方案1】:

Pool 类中定义 iteratorconst_iterator。 Boost 算法库是在 C++11 之前编写的,它不使用decltype 来查找迭代器类型。不幸的是,如果可用,它仍未更新以使用它,因此您必须定义迭代器类型。无论如何,这是一个很好的做法。

template <class T>
class Pool
{
public:
    Pool() : container_{}
    {};
public:
    using iterator = typename std::vector<T>::iterator;
    using const_iterator = typename std::vector<T>::const_iterator;

    iterator begin() { return container_.begin(); }
    iterator end()   { return container_.end(); }
    const_iterator begin() const { return container_.begin(); }
    const_iterator end()   const { return container_.end(); }
private:
    std::vector<T>     container_;     ///< holder of elements
};

【讨论】:

  • 您可以更进一步,定义 C++ 标准容器往往具有的无数 typedef:en.cppreference.com/w/cpp/container/forward_list -- 前向列表有一个非常小的集合。它们中的大多数似乎与您可以从 std::iterator_traits&lt;iterator&gt; 获得的相同。
  • 宾果游戏! iteratorconst_iterator 都是必需的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多