ADL和容器功能(开始,结束等)

时间:2016-12-06 10:24:31

标签: c++

C ++ 11及更高版本在命名空间std中定义了自由函数beginendempty等。对于大多数容器,这些函数调用相应的成员函数。但对于某些容器(如valarray),这些自由函数会被重载(initializer_list没有成员begin())。因此,要迭代任何容器,应该使用自由函数,并且应该使用除std之外的名称空间来查找容器的函数:

 template<typename C>
 void foo(C c)
 {
   using std::begin;
   using std::end;
   using std::empty;

   if (empty(c)) throw empty_container();
   for (auto i = begin(c); i != end(c); ++i) { /* do something */ }
 }

问题1 :我说错了吗?期望通过ADL找到beginend吗?

但是ADL规则指定如果参数的类型是类模板特化,则ADL包括所有模板参数的名称空间。然后Boost.Range库发挥作用,它定义boost::beginboost::end等。这些函数定义如下:

template< class T >
inline BOOST_DEDUCED_TYPENAME range_iterator<T>::type begin( T& r )
{
    return range_begin( r );
}

如果我使用std::vector<boost::any>和Boost.Range,我会遇到麻烦。 std :: begin和boost :: begin重载是不明确的。它,我不能编写通过ADL找到免费begin的模板代码。如果我明确使用std::begin,我希望任何非std::容器都有成员begin

问题2 :在这种情况下我该怎么办?

依靠成员函数的存在?最简单的方法。

Ban Boost.Range?嗯,采用容器而不是一对迭代器的算法很方便。 Boost.Range适配器(懒惰地将算法应用于容器的容器)也很方便。但是如果我不在我的代码中使用Boost.Range,它仍然可以在boost库中使用(除了Range)。这使得模板代码非常脆弱。

Ban Boost?

1 个答案:

答案 0 :(得分:0)

几年前,我遇到了类似的问题,我的代码突然开始在std::beginboost::begin之间产生歧义。我发现它是由于使用Boost.Operator来帮助定义一个类,即使它甚至不是公共基类,也不是所涉及类型的用户明显的。某处的随机更改导致#include <boost/range/begin.hpp>出现在某处的嵌套包含文件中,从而使boost::begin对编译器可见。

我向邮件列表抱怨直接将类放在Boost命名空间中,而不是嵌套类中,并通过using声明公开它们;直接在Boost命名空间中定义的所有内容都可能通过意外ADL相互踩到一起。

我今天只是尝试重现这一点,现在看起来对这种模棱两可的态度非常有弹性!查看定义,boost::begin本身位于内部命名空间中,因此如果您未在自己的范围内提供自己的using boost::begin;,则可以通过非限定查找永远找到它。 / p>

我不知道这个修复发生了多久。 (如果您仍然可以重现它,请发布包含版本和平台详细信息的完整程序。)

所以你的答案是:

对于Boost,不要再担心了(必要时升级Boost)。

对于新代码,永远不要在与其任何已定义类型相同的命名空间中定义名为begin的自由函数。