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