当使用 lambda 参数时,ADL 失败了?

4

相当长一段时间以前,我注意到在Visual C++ 10中,当至少有一个参数是lambda表达式时,ADL会失败。

std::vector<float> vec;
for_each(begin(vec), end(vec), [](float) {}); 

上述代码在VC++10和11(beta)上无法编译(通过ADL找到begin和end)。当我将lambda函数转换为常规自由函数时,一切都按预期工作。
我曾在Herb Sutters博客上提问过,并阅读了一些关于msdn connect的帖子和通常的答案是:这是一个错误,我们尚未实现最新标准的lambda,这在当时是可以理解的。事情还没有成型。在MS connect上也有令人不安的评论,即这将不会在下一个版本即vc 11中解决。
我的问题是,这段代码是否符合C++11标准?我无法完全理解。当我使用lambda时,我是否真的需要在for_each和其他算法前加上std::前缀?
我有点怀疑在vc++11发布后,这种行为不会改变。

1
你使用哪个免费函数? - Johannes Schaub - litb
就像void f(float) {}这样简单的东西也能工作。或者在函数调用之外声明lambda:auto f = {}; - Martin Wirth
2个回答

15

标准不能保证你想要的结果。

考虑以下情况,我们可以很容易地意识到在类似于您帖子中提供的示例的情况下,并没有任何保证ADL会起作用。


  • std::begin (c)/std::end (c)

    这些函数在标准中的描述如下:

template <class C> auto begin(C& c) -> decltype(c.begin());
template <class C> auto end(C& c) -> decltype(c.end());

Container< ... >::iterator(即c.begin()的返回类型)是一种实现定义的类型。

更多信息可以在24.5.6 Range Access23.3.6.1/2类模板vector (或任何其他模板STL容器)中阅读到。

  • [](){} - Lambda表达式

    Lambda表达式是一种实现定义的类型,标准中没有规定生成的对象将是namespace std下的类型。

    只要符合标准设置的其他规则,它几乎可以存在于任何地方。



  • 太长不看

    std::begin/std::end/lambda表达式产生的类型不能保证属于namespace std,因此ADL不能保证启动。


    3
    @David:不,这个问题确切地涉及for_each。它甚至说beginend没有问题。我认为大多数人没有完全正确地阅读问题。对于refp:+1,这是正确的答案。 - Xeo
    @Xeo:我认为ADL无法解析beginend。无论如何,由于标准中没有保证迭代器将在std中,因此如果lambda被替换为函数调用,则在未能在lambda情况下找到for_each(假设std::vector<>::iterator恰好定义在std中)或者在ADL找到for_each时存在错误(如果std::vector<>::iterator未在std内定义),仍然存在错误。 - David Rodríguez - dribeas
    澄清一下:在vc++10和11 beta中,通过ADL正确解析了begin和end。我的问题是为什么当有一个或多个lambda来自std命名空间时,for_each(或任何其他std算法)的ADL会失败。我认为当存在lambda参数时,vc++会禁用ADL。请注意,如果您将在for_each函数调用之外声明的lambda传递给for_each,则ADL可以正常工作(这是微软目前的官方解决方法)。 - Martin Wirth
    @refp:感谢您的澄清!正如我上面所说,我担心lambda和非lambda参数的组合。不进行限定也有其缺点,就像您指出的那样。例如,std::array<>类型的参数与begin()和end()结合使用不会返回命名空间std中定义的迭代器,因此在这种情况下依赖ADL查找for_each()肯定会失败,这是可以预料的。尽管如此,我的原始问题是针对另一个问题的。 - Martin Wirth
    @refp: 那并不是我的批评 :) 我只是更多地考虑了结合lambda和非lambda函数参数的问题 - 这显然会导致在vc ++中停止ADL的工作。我同意您关于资格使用的所有观点,以及您的观点,即begin和end不需要在std ::内返回任何内容。我可能不应该在我的例子中提到begin和end。我只是用这些因为for_each和begin和end经常一起使用。我应该想出另一个完全不使用标准算法的例子。 - Martin Wirth
    显示剩余3条评论

    1

    这是完全有效的代码。任何没有错误的编译器都能够编译它。但由于MSVC存在缺陷,因此无法通过ADL搜索函数,那么您可能不应该依赖ADL,而是使用std::来限定它,帮助编译器找到函数。


    这证实了我的想法。我也不明白为什么lambda参数和非lambda参数的组合会禁用ADL。不确定我是否喜欢Microsoft官方的响应(在其他地方实例化lambda或使用std限定符)。;) - Martin Wirth
    @Martin:不,'vector<T>::iterator' 允许是 'T *' (我怀疑这恰恰是问题所在)。虽然 @refp 的答案有点长,而且没有确认你的怀疑,但它是正确的。尽管如此,我无法解释为什么它与未参数化 std 类型的自由函数一起使用时可以工作。 - Potatoswatter

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接