C++ 做C++;lambda未正确选择重载函数?
我有一个函数,它遍历一个容器,并将每个元素传递给一个谓词进行过滤。此函数的重载还将每个元素的索引传递给谓词C++ 做C++;lambda未正确选择重载函数?,c++,visual-c++,lambda,c++11,visual-c++-2012,C++,Visual C++,Lambda,C++11,Visual C++ 2012,我有一个函数,它遍历一个容器,并将每个元素传递给一个谓词进行过滤。此函数的重载还将每个元素的索引传递给谓词 template<typename TContainer> void DoSomethingIf(TContainer &c, std::function<bool (const typename TContainer::const_reference)> predicate); template<typename TContainer> vo
template<typename TContainer>
void DoSomethingIf(TContainer &c, std::function<bool (const typename TContainer::const_reference)> predicate);
template<typename TContainer>
void DoSomethingIf(TContainer &c, std::function<bool (const typename TContainer::const_reference, int)> predicate);
模板
void DoSomethingIf(TContainer&c,std::函数谓词);
模板
void DoSomethingIf(TContainer&c,std::函数谓词);
我发现,尝试使用裸lambda调用其中任何一个函数都会导致VC11中出现编译器错误,而使用std::function对象则会成功:
void foo()
{
std::vector<int> v;
// fails
DoSomethingIf(v, [](const int &x) { return x == 0; });
// also fails
auto lambda = [](const int &x) { return x == 0; };
DoSomethingIf(v, lambda);
// success!
std::function<bool (const int &)> fn = [](const int &x) { return x == 0; };
DoSomethingIf(v, fn);
}
1>c:\users\moswald\test.cpp(15): error C2668: 'DoSomethingIf' : ambiguous call to overloaded function
1> c:\users\moswald\test.cpp(8): could be 'void DoSomethingIf<std::vector<_Ty>>(TContainer &,std::function<_Fty>)'
1> with
1> [
1> _Ty=int,
1> TContainer=std::vector<int>,
1> _Fty=bool (const int &,int)
1> ]
1> c:\users\moswald\test.cpp(5): or 'void DoSomethingIf<std::vector<_Ty>>(TContainer &,std::function<_Fty>)'
1> with
1> [
1> _Ty=int,
1> TContainer=std::vector<int>,
1> _Fty=bool (const int &)
1> ]
1> while trying to match the argument list '(std::vector<_Ty>, foo::<lambda_8EADDE04A8D35A3C>)'
1> with
1> [
1> _Ty=int
1> ]
1>c:\users\moswald\test.cpp(19): error C2668: 'DoSomethingIf' : ambiguous call to overloaded function
1> c:\users\moswald\test.cpp(8): could be 'void DoSomethingIf<std::vector<_Ty>>(TContainer &,std::function<_Fty>)'
1> with
1> [
1> _Ty=int,
1> TContainer=std::vector<int>,
1> _Fty=bool (const int &,int)
1> ]
1> c:\users\moswald\test.cpp(5): or 'void DoSomethingIf<std::vector<_Ty>>(TContainer &,std::function<_Fty>)'
1> with
1> [
1> _Ty=int,
1> TContainer=std::vector<int>,
1> _Fty=bool (const int &)
1> ]
1> while trying to match the argument list '(std::vector<_Ty>, foo::<lambda_8EADDE04A8D35A3D>)'
1> with
1> [
1> _Ty=int
1> ]
void foo()
{
std::向量v;
//失败
DoSomethingIf(v,[](const int&x){return x==0;});
//也失败了
自动lambda=[](常量int&x){return x==0;};
DoSomethingIf(v,λ);
//成功!
函数fn=[](const int&x){return x==0;};
DoSomethingIf(v,fn);
}
1> c:\users\moswald\test.cpp(15):错误C2668:'DoSomethingIf':对重载函数的调用不明确
1> c:\users\moswald\test.cpp(8):可以是“void DoSomethingIf(TContainer&,std::function)”
1> 与
1> [
1> _Ty=int,
1> t容器=标准::向量,
1> _Fty=bool(常数int和,int)
1> ]
1> c:\users\moswald\test.cpp(5):或“void DoSomethingIf(TContainer&,std::function)”命令
1> 与
1> [
1> _Ty=int,
1> t容器=标准::向量,
1> _Fty=bool(const int&)
1> ]
1> 尝试匹配参数列表时“(std::vector,foo::)”
1> 与
1> [
1> _Ty=int
1> ]
1> c:\users\moswald\test.cpp(19):错误C2668:'DoSomethingIf':对重载函数的调用不明确
1> c:\users\moswald\test.cpp(8):可以是“void DoSomethingIf(TContainer&,std::function)”
1> 与
1> [
1> _Ty=int,
1> t容器=标准::向量,
1> _Fty=bool(常数int和,int)
1> ]
1> c:\users\moswald\test.cpp(5):或“void DoSomethingIf(TContainer&,std::function)”命令
1> 与
1> [
1> _Ty=int,
1> t容器=标准::向量,
1> _Fty=bool(const int&)
1> ]
1> 尝试匹配参数列表时“(std::vector,foo::)”
1> 与
1> [
1> _Ty=int
1> ]
这是意料之中的事吗?是否有不同的方法来重载这些函数(除了将其中一个函数重命名为“
DoSomethingIfWithIndex
”?重载模糊是意料之中的
std::function
有一个可接受任何参数的转换构造函数模板。只有在实例化构造函数模板后,编译器才能确定它将拒绝该参数
在第一个和第二个示例中,将未指定的lambda类型转换为每个std::function
类型都需要用户定义的转换。这两种转换都不是更好的(它们都是用户定义的转换),因此编译器报告重载歧义
在第三个示例(有效的示例)中,没有歧义,因为没有使用
std::function
构造函数模板。相反,使用了它的副本构造函数(并且,在所有其他条件相同的情况下,非模板优先于模板).std::function
在二进制定界中有它的用途,但不是作为函子的通用参数。正如您刚才发现的,它的转换构造函数与重载解析的交互很差(这与lambda表达式无关).由于DoSomethingIf
已经是一个模板,我认为接受广义函子的规范解决方案没有问题:
template<typename TContainer, typename Predicate>
void DoSomethingIf(TContainer& c, Predicate&& predicate);
这仍然存在一个恼人的问题,即如果有人传递了一个不满足我们条件的谓词(或任何东西),我们会得到一个“找不到匹配函数”错误(重载解析失败),而不是一个有用的错误。如果要解决这个问题,可以添加一个“全面覆盖”重载:
template<
typename Container
, typename Predicate
, typename = typename std::enable_if<
!is_callable<Predicate, bool(typename Container::const_reference)>::value
&& !is_callable<Predicate, bool(typename Container::const_reference, int)>::value
>::type
// more dummies
, typename = void, typename = void
>
void DoSomethingIf(Container&, Predicate&&)
{ static_assert( dependent_false_type<Container>::value,
"Put useful error message here" ); }
模板<
类型名容器
,类型名谓词
,typename=typename std::如果<
!是否可调用::值
&&!是否可调用::值
>::类型
//更多的傻瓜
,typename=void,typename=void
>
void DoSomethingIf(容器&,谓词&)
{static_assert(dependent_false_type::value),
“在此处放置有用的错误消息”);}
(dependent\u false\u type
必须是从std::false\u type
继承的类型,我们不能简单地static\u assert
,也不能像我们所希望的那样,每次都触发false
,而不仅仅是在实例化模板时。或者,如果需要,您可以重复std::enable\u中的条件代码>此处,它在某种程度上可以作为代码内部的文档,但不能改进功能本身。)
剩下的就是在哪里可以找到是可调用的
,因为它实际上不是一个标准特性。如果您以前编写过SFINAE测试,那么它相对容易实现,但是有点乏味,因为您必须部分地专门化void
返回。我不在这里专门化,因为这个答案已经足够长了
如果您发现此解决方案功能强大但过于冗长,那么您可能会喜欢以下概念:)是有道理的,但令人失望。这似乎意味着永远没有办法通过不同的lambda选择重载函数。不使用std::function
,不可以。对于无捕获的lambda,您可以使用可调用特性或依赖函数指针衰减,但总的来说,这是个麻烦。如何使用可调用tr要解决这样的问题吗?如果可能的话,我希望能够传递与签名匹配的任何函数对象,而不仅仅是自由函数/lambdas。谢谢。是的,我花了一些时间查看type\u traits
标题,试图找到一个
template<
typename Container
, typename Predicate
, typename = typename std::enable_if<
!is_callable<Predicate, bool(typename Container::const_reference)>::value
&& !is_callable<Predicate, bool(typename Container::const_reference, int)>::value
>::type
// more dummies
, typename = void, typename = void
>
void DoSomethingIf(Container&, Predicate&&)
{ static_assert( dependent_false_type<Container>::value,
"Put useful error message here" ); }