std :: function重载具有类似的转换

时间:2015-06-15 19:00:05

标签: c++ c++11

我正在为学习目的编写类似STL的库。所有集合都扩展了一个名为Iterable的类,其中包含<algorithm>中所有函数的包装函数。例如,它允许vec.each([](T t){...});我强烈优先于详细std::for_each。给我带来问题的函数是count - 我想重载Iterable::count所以它根据参数类型结合std::countstd::count_if的行为但是我遇到了一个奇怪的错误。

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

我应该注意,将count_if包装函数名称更改为count_if确实有效并解决了歧义,但我更愿意将它们命名为count并找出原因首先是含糊不清。

根据我的解释,编译器正在尝试使用std::function ctor创建一个新的template <class F> function(F f),然后遇到歧义。是这样的吗?这看起来很奇怪,因为下面的行也无法编译。

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

非常感谢任何见解或潜在修复。

忘了说;使用visual studio 2012,nov 2012 ctp编译器

2 个答案:

答案 0 :(得分:4)

在没有勘误的已发布的C ++ 11标准中,

std::function<Sig>包含一个构造函数,认为它可以消耗任何东西,就其签名而言。如果你传递它不能消耗的东西(不可调用的东西),它就无法编译。

过载解决方案比编译失败更早(基于较浅的信息)发生。它匹配签名,而不是实现。

提出了错误报告和修复程序,因此一些C ++ 11编译器可以解决这个问题,并且所有C ++ 14编译器都必须解决此问题。

VS2012具有有限的SFINAE过载分辨率功能。但是一种方法看起来像:

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{};

这是对特质类的尝试,告诉您F(Arg)bool - 返回&#34;过滤&#34;关于类型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) );
}

但我不知道MSVC2012会与上述相关。

在这里,我使用标签调度来选择我调用的count版本。特征类is_filter_on进行测试以确定伪表达式F(Arg)是否类似于过滤器。如果是,我们会发送到std::count_if。否则,我们会发送到std::count版本。

答案 1 :(得分:2)

问题在于0在这里是不明确的,它可以被解释为空指针或int,这使得它与std::function构造函数和更一般的const T& value都匹配(两者都需要转换)。

如果您不想更改界面,可以创建一个非常简单的函数模板来推导和分派参数。

C ++ 11版本:

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

这是因为函数模板类型推导规则没有这种歧义,它们从不将0视为空指针。

所以你的界面现在是:

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));
}

你可以自然地使用它:

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
}