做 C++ lambdas 没有正确选择重载函数?
我有一个函数可以迭代容器并将每个元素传递给谓词进行过滤。此函数的重载还将每个元素的索引传递到谓词中。
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);
我发现尝试使用裸 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> ]
这是预期的吗?是否有一种不同的方法来重载这些函数(除了将其重命名为“DoSomethingIfWithIndex
”?
I've got an function that iterates over a container and passes each element to a predicate for filtering. An overload of this function also passes the index of each element into the predicate.
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);
I have found that attempting to call either of these functions with a naked lambda will cause a compiler error in VC11, while using a std::function object will succeed:
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> ]
Is this to be expected? Is there a different way to overload these functions (short of renaming one to be "DoSomethingIfWithIndex
"?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
过载歧义是预期的。
std::function
有一个接受任何参数的转换构造函数模板。只有在实例化构造函数模板之后,编译器才能确定它将拒绝该参数。在第一个和第二个示例中,需要用户定义的转换才能将未指定的 lambda 类型转换为每种
std::function
类型。这两种转换都不是更好(它们都是用户定义的转换),因此编译器会报告重载歧义。在您的第三个示例(有效的示例)中,没有任何歧义,因为未使用
std::function
构造函数模板。相反,使用它的复制构造函数(并且,在所有其他条件相同的情况下,非模板优于模板)。The overload ambiguity is expected.
std::function
has a converting constructor template that accepts any argument. Only after the constructor template is instantiated can the compiler determine that it will reject the argument.In both your first and second examples, a user-defined conversion is required to convert the unspecified lambda type to each of the
std::function
types. Neither conversion is better (they are both user-defined conversions), so the compiler reports the overload ambiguity.In your third example (the one that works), there is no ambiguity because the
std::function
constructor template is not used. Instead, its copy constructor is used (and, all other things being equal, nontemplates are preferred over templates).std::function
可用于二进制分隔,但不能作为函子的通用参数。正如您刚刚发现的,它的转换构造函数与重载解析交互得很差(这与 lambda 表达式无关)。由于DoSomethingIf
已经是一个模板,因此我认为接受广义函子的规范解决方案没有问题:不过您可能会注意到,这个版本不能重载并且简单地接受任何东西作为谓词,甚至
int
。像往常一样,使用 SFINAE 可以轻松解决重载:这仍然存在一个恼人的问题,即如果有人传递不满足我们条件的谓词(或实际上任何东西),我们会收到“找不到匹配函数”错误(重载解析失败) )而不是一个有用的错误。如果你想解决这个问题,你可以添加一个“catch-all”重载:(
dependent_false_type
只需是一个继承自std::false_type
的类型,我们可以'tstatic_assert
简单地false
或将每次触发,而不是像我们希望的那样仅在实例化模板时触发。你可以重复我们的条件在std::enable_if
内部,它在某种程度上作为代码内部的文档,但并没有改进功能本身。)剩下的就是在哪里找到
is_callable< /code>,因为它实际上不是标准特征。如果您以前编写过 SFINAE 测试,那么实现起来相对容易,但有点乏味,因为您必须部分专门针对
void
返回。我不会在这里进行专门讨论,因为这个答案已经足够长了。如果您发现这个解决方案功能强大但过于冗长,那么也许您会喜欢概念:)
std::function
has its uses at binary delimitations, but not as a general-use parameter for functors. As you've just discovered, its converting constructor interacts badly with overload resolution (and that has nothing to do with lambda expressions). SinceDoSomethingIf
is already a template, I don't see a problem with the canonical solution of accepting generalized functors:As you may notice though, this version can't be overloaded and simply will accept anything as a predicate, even
int
. Overloading is easily solved with SFINAE, as usual:This still has the annoying issue that if someone passes a predicate (or really anything) that doesn't satisfy our conditions, we get a 'no matching function found' error (an overload resolution failure) rather than a helpful error. If you want to solve that, you can add a 'catch-all' overload:
(
dependent_false_type
just has to be e.g. a type inheriting fromstd::false_type
, we can'tstatic_assert
on simplyfalse
or that will be triggered every time, not just when the template is instantiated, as we'd like. Alternatively you can repeat the condition we have insidestd::enable_if
here, which works somewhat as documentation inside the code, but doesn't improve the functionality itself.)All that remains is where to find
is_callable<Functor, Signature>
, as it's not in fact a Standard trait. It's relatively easy to implement if you've ever written an SFINAE test before, but a bit tedious as you have to partially specialize forvoid
returns. I'm not putting a specialization here as this answer is long enough as it is.If you find this solution powerful but too verbose, then perhaps you'd enjoy concepts :)