2

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?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
lotfishtaine
  • 111
  • 1
  • 3
  • 10
  • 2
    It is important when asking a question on stack overflow that you provide enough information for it to be answered. The first step you should do is try to reduce your problem down to a smaller one. You don't understand how `erase` and `remove_if` work; so create a small program that uses those functions, and see if you can get the same error. You can do this either by starting with your full program and cutting parts out, or start with an empty program and add parts in. Once you have a SIMPLE, SELF-CONTAINED and COMPLETE example, you can then ask about that problem. – Yakk - Adam Nevraumont Sep 15 '15 at 17:48
  • 1
    @T.C. The OP answered your question in comment below: `std::is_empty` is what she is trying to use. Good old `using namespace std`, isn't it horrible? – Yakk - Adam Nevraumont Sep 15 '15 at 18:03

1 Answers1

6

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Technically, it has an `operator()` - inherited from `integral_constant` :) – T.C. Sep 15 '15 at 18:04
  • and coming in a TS near you real soon: uniform erasure with: `erase_if(tab, container_is_empty)` – TemplateRex Sep 15 '15 at 18:31
  • 1
    @TemplateRex I have my own version of that already, so no need to wait. As a bonus, my `erase_if` lets you modify entries as you iterate as well! (simply by saying "it is well defined", and not calling `std::erase_if` (sadly), while the standard says "no cookie for you" if you call `std::erase_if`). – Yakk - Adam Nevraumont Sep 15 '15 at 18:36