我正在为学习目的编写类似STL的库。所有集合都扩展了一个名为Iterable
的类,其中包含<algorithm>
中所有函数的包装函数。例如,它允许vec.each([](T t){...});
我强烈优先于详细std::for_each
。给我带来问题的函数是count
- 我想重载Iterable::count
所以它根据参数类型结合std::count
和std::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编译器
答案 0 :(得分:4)
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
}