c++c++11vectoreraseremove-if

How do delete empty cells of a vector if using remove_if


I am filling a std::vector with some object.

this vector instance as defined in the following line:

std::vector< std::list< pcl::PointXYZRGB>> tab;

I now want to delete the empty cells. I tried like following:

tab.erase(remove_if(tab.begin(), tab.end(), std::is_empty), tab.end());

I am getting the following error:

error: missing template arguments before ‘)’ token
     voxels.erase(remove_if(voxels.begin(), voxels.end(), is_empty**)**, voxels.end());

I am confused, can someone tell me how I can do that?


Solution

  • std::is_empty is a traits template. std::is_empty<X> tells you if the type X is an empty type -- that is, it is a struct {} or class {} with nothing inside it.

    This is not what you want. First, because it cannot be called the way you need it to be called -- it has no operator() that is useful to you -- and second, it takes a type, not an instance of a type, and answers questions about the type, not the instances.

    auto list_is_empty = [](std::list<pcl::PointXYZRGB> const& l) {
      return l.empty();
    };
    tab.erase(std::remove_if(tab.begin(), tab.end(), list_is_empty), tab.end());
    

    will solve your problem. A better way to solve it would be:

    struct container_is_empty_t {
      template<class C>
      bool operator()(C const& c)const{
        return c.empty();
      }
      // not really needed, arrays of size 0 are non-standard
      template<class T, size_t N>
      bool operator()(T(&)[N])const{
        return N > 0;
      }
    };
    static container_is_empty_t const container_is_empty;
    

    in some header file with utility code, then:

    tab.erase(std::remove_if(tab.begin(), tab.end(), container_is_empty), tab.end());
    

    does the same thing, but without being hard-coded to only work on std::list< pcl::PointXYZRGB>s.

    Or in :

    tab.erase(std::remove_if(tab.begin(), tab.end(), [](auto&& c){ return c.empty(); }, tab.end());
    

    WARNING

    When doing the erase-remove idiom, if you forget the tab.end() part, you'll get code that compiles but does the completely wrong thing in a pathological way.

    I find it best to have

    // erases elements from sequential container C if f(element) is true:
    template<class C, class F>
    auto erase_if( C& c, F f ) {
      using std::begin; using std::end;
      auto it = std::remove_if( begin(c), end(c), std::move(f) );
      auto sz = std::distance( it, end(c) );
      c.erase( it, end(c) );
      return sz;
    }
    

    helper function to avoid that danger.

    Then you get:

    erase_if( tab, [](auto&& c){ return c.empty(); } );
    

    which is nice and short.

    The standard adds std::erase_if to the language.