Std :: overload functions have similar conversions

I am about to write an STL library for training. All collections extend a class called Iterable

, which contains wrapper functions for all functions found in <algorithm>

. For example, it allows vec.each([](T t){...});

, which I prefer over verbose std::for_each

. The function that is giving me problems is count

- I want to overload Iterable::count

so that it combines behavior both std::count

and std::count_if

depending on the type of the argument, but I ran into a weird error.

Iterable.h

virtual int count(const T& value) const {
    return std::count(begin(), end(), value);
}
virtual int count(std::function<bool(T&)> predicate) {
    return std::count_if(begin(), end(), predicate);
}
virtual int count(std::function<bool(const T&)> predicate) const {
    return std::count_if(begin(), end(), predicate);
}

      

main.cpp

Vector<int> vec; // extends Iterable
vec.add(0);
vec.add(1);
vec.count([](int i){ return i == 0; }); // compiles and works fine
vec.count(0); // error c2666: 3 overloads have similar conversions

      

I should note that changing the name of count_if

the wrapper function count_if

does not work and disambiguates, but I would prefer them to be named count

and also figure out why there is ambiguity in the first place.

From what I am interpreting, the compiler tries to create a new one std::function

using the template <class F> function(F f)

ctor and then runs into ambiguity. This is true? It seems odd since the line below won't compile either.

std::function<bool(int)> f(0); // error C2064: term does not evaluate to a function taking 1 arguments

      

Any ideas or potential fixes are greatly appreciated.

I forgot to say; using visual studio 2012 november 2012 ctp compiler

+3


source to share


2 answers


The problem is that 0 is ambiguous here, it can be interpreted as a null pointer or an int, making it match both the constructor std::function

and the more general one const T& value

(both require conversion).

If you don't want to change the interface, you can simply create a very simple function template to output and send arguments.

C ++ 11 version:

template<typename U>
int count(U&& value) const {
    return count_impl(std::forward<U>(value));
}

      

This works because the function template type inference rules do not have this ambiguity, they never treat 0 as a null pointer.



So now your interface is:

virtual int count_impl(const T& value) const {
    return std::count(v.begin(), v.end(), value);
}
virtual int count_impl(std::function<bool(T&)> predicate) {
    return std::count_if(v.begin(), v.end(), predicate);
}
virtual int count_impl(std::function<bool(const T&)> predicate) const {
    return std::count_if(v.begin(), v.end(), predicate);
}

template<typename U>
int count(U&& value) const {
    return count_impl(std::forward<U>(value));
}

      

And you can use it naturally:

int main(){
    Vector<int> vec; // extends Iterable
    vec.count([](int i){ return i == 0; }); // compiles and works fine
    vec.count(0); // no problem, calls virtual int count_impl(const T& value) const
}

      

+2


source


std::function<Sig>

in the published C ++ 11 standard, there is no error containing a constructor that thinks it can consume anything as far as its signature is concerned. If you pass it to things that it cannot use (things that are not callable), it won't compile.

Overload resolution occurs earlier (based on less information) than compilation fails. It matches signatures, not implementations.

A bug report and fix was suggested, so some C ++ 11 compilers might fix this, and all C ++ 14 compilers should fix it.

VS2012 has limited SFINAE overload resolution capabilities. But one approach would look like this:

template<class Sig, class=void>
struct is_filter_on : std::false_type{};
template<class F, class Arg>
struct is_filter_on< F(Arg),
  typename std::enable_if<std::is_convertible<
    typename std::result_of<F(Arg)>::type
  ,bool>::value>::type
> : std::true_type{};

      



which is an attempt at a feature class that tells you what F(Arg)

is a- bool

validated "filter" on type values Arg

.

template<class X>
size_t count(X&& x) const {
  return count( std::forward<X>(x), is_filter_on< X&(T const&) >{} );
}
template<class X>
size_t count(X&& x) {
  return count( std::forward<X>(x), is_filter_on< X&(T&) >{} );
}

template<class F>
size_t count(F&& f, std::true_type) const {
  return std::count_if( begin(), end(), std::forward<F>(f) );
}
template<class F>
size_t count(F&& f, std::true_type) {
  return std::count_if( begin(), end(), std::forward<F>(f) );
}
template<class X>
size_t count(X&& x, std::false_type) const {
  return std::count( begin(), end(), std::forward<X>(x) );
}
template<class X>
size_t count(X&& x, std::false_type) {
  return std::count( begin(), end(), std::forward<X>(x) );
}

      

but I have no idea if MSVC2012 will work with the above.

Here I am using label dispatch to pick which version count

I am calling. The feature class is_filter_on

runs a test to determine if the pseudo-expression F(Arg)

is filterable. If so, send to std::count_if

. Otherwise, we'll go to version std::count

.

+4


source







All Articles