4

I have a vector of vectors, and I want to check if all of them are empty. Using the standard library, I tried:

#include <algorithm>
#include <vector>

int main()
{
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), std::empty);
}

This result in the following error in clang 7.0:

/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_algo.h:508:5: note: candidate template ignored: couldn't infer template argument '_Predicate'

I guess it's a standard behavior, due to the rules of type deduction. But anyway, what i the easiest way to workaround this?

EDIT: I accepted rubenvb's answer, because he gave a simple and reasonable explanation, together with the natural workaround. all_of accepts a predicate, which is a function, a function object or a lambda expression. std::empty is neither of those, but a function template. When explicitly instantiating it, we get a plain function which should work. Surprisingly, it's still not compile on most compilers I tried.

Well, lets see:

on GCC 6.3, it compiles just fine - https://godbolt.org/g/Pxta7C

but on GCC from trunk, it causes an internal compiler error - https://godbolt.org/g/H6DHt5

Neither Clang from trunk or MSVC 2017 succeed to compile it:

https://godbolt.org/g/819pbQ (Clang)

https://godbolt.org/g/ua5E8e (MSVC)

EDIT2: Apparently, Robert Andrzejuk is right too: the reason that the compiler cannot handle it is an ambiguous overload resolution. std::empty has 3 different overloads. and two of them are equally well candidates: the general one and the std::initializer list one. I achieved similar results with the following minimal version:

#include <vector>

template<class T>
void foo(const T& t);

template<class T>
void foo(const std::initializer_list<T>& il);

template<class F>
void bar(F f);


int main()
{
   bar(foo<std::vector<int>>);
}

There is one difference, though. This example simply not compile in GCC from trunk (instead of causing an ICE).

  • 1
    but what is the error ? what you have posted is the note – sop May 28 '18 at 12:54
  • `std::empty` is a template. The third parameter to `all_of` is not a template, but a closure, or a callable object. You must instantiate the template: `std::empty>{}`. It's possible that just `std::empty{}` would work too, haven't bothered to check. – Sam Varshavchik May 28 '18 at 12:56
  • Thanks. But still, it's a *function template*, not a class template. So neither of this works. – Shmuel Hanoch May 28 '18 at 13:31
  • @Shmuel you are correct, see my answer. Just drop the brace initialization `{}`. – rubenvb May 28 '18 at 14:59

5 Answers5

2

Unfortunately there is a problem to distinguish overloaded template functions as std::all_of is also a template function. Better explaination: std::function fails to distinguish overloaded functions

So to do this, a static_cast to the correct function type: bool ( * )( const std::vector< int >& ) is required:

std::all_of( vv.begin(), vv.end(),
             static_cast< bool ( * )( const std::vector< int >& ) >( std::empty ) );

Using knowledge about the required static_cast we can make a helper template function to deduce the correct definition from the container type:

The helper function:

template< typename C >
inline auto overloaded_pred_for( const C&, bool ( *f )( const C& ) ) -> decltype( f )
{
    return f;
}

Example of it's usage:

std::all_of( vv.begin(), vv.end(), 
             overloaded_pred_for( std::vector< int >(), std::empty ) );
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31
1

fast workaround

#include <algorithm>
#include <vector>

int main() {
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), 
    [](const auto &v) {return std::empty(v);});
}
Andrey Semenov
  • 901
  • 11
  • 17
1

what i the easiest way to workaround this?

Maybe

std::all_of(std::begin(vv), std::end(vv),
            [](auto const & v){ return v.empty(); });

?

max66
  • 65,235
  • 10
  • 71
  • 111
  • 1
    @sop - do you mean `std::empty(v)`? I suppose it would work but `std::empy(v)` call `v.empty()` so I think is better call `v.empty()` directly (or avoid the lambda calling `std::empy()` without the use of a lambda) – max66 May 28 '18 at 14:04
1

std::empty is a function template, and as such not a callable object in and of itself. By explicitly providing the template parameters, you turn the function template name into a concrete instantiation, which is callable:

#include <algorithm>
#include <iterator>
#include <vector>

int main()
{
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), std::empty<std::vector<int>>);
}

Live demo (which incidentally crashes the compiler). Also note this GCC version apparently needed an #include <iterator> even though it is explicitly mentioned std::empty should come in with e.g. #include <vector>...

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • Sound reasonable, still it cause an ICE on gcc (trunk), and simply not compile in clang 7. Is it a bug on both compilers? – Shmuel Hanoch May 28 '18 at 15:40
  • @ShmuelHanoch huh that's quite odd. Doing it with another function template (that emulates `std::empty` in a very limited number of cases) does work, so at least I'm confident the syntax is correct, but obviously is not what you want: http://coliru.stacked-crooked.com/a/a5f1734b3175aeaf. Strange that is seems broken on all of the Big Three. – rubenvb May 28 '18 at 15:57
  • This does not work on the big 3 compilers (gcc, clang, msvc - https://godbolt.org/g/AAKdYh ). See my answer for a reason why. – Robert Andrzejuk Jun 01 '18 at 11:09
  • @ShmuelHanoch please unaccept this answer so I can delete it. I was mistaken and unaware that std::empty is an overloaded function (and that this would be problematic). – rubenvb Jun 01 '18 at 11:17
-1

The problem is that there is not such a thing as std::empty, it's a member function as far as I know. Try using [](const auto& i){ return i.empty(); } instead.

EDIT: well, I haven't seen std::empty before but as the commenter below pointed out it exists, but still you can use the lambda

Qizot
  • 9
  • 2