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

C ++ 11及更高版本在命名空间标准中定义了自由函数beginendempty等。 对于大多数容器来说,这些函数调用相应的成员函数。 但对于一些容器(比如valarray ),这些自由函数被重载(initializer_list没有成员begin())。 因此,要迭代任何容器free函数应使用从比其他命名空间的集装箱功能std ADL应使用:

 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 :我正确吗? 正在beginend有望通过ADL被发现?

但是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:: container都有一个成员begin

问题2 :在这种情况下我该做什么?

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

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

禁用Boost?


几年前,我有一个类似的问题,我的代码突然开始在std::beginboost::begin之间产生歧义。 我发现这是因为使用Boost.Operator来帮助定义一个类,尽管它甚至不是公共基类,或者对于涉及的类型的用户来说都是显而易见的。 某处发生随机变化会导致#include <boost/range/begin.hpp>出现在某处的嵌套包含文件中,从而使boost::begin对编译器可见。

我抱怨有关直接在boost名字空间类的投入,而不是一个嵌套类,并通过暴露他们的邮件列表using声明; 直接在Boost命名空间中定义的所有东西都可能通过偶然的ADL彼此相连。

我只是试图在今天重现这一点,而现在看起来它对于这种歧义似乎颇具适应力! 看看定义, boost::begin本身就在一个内部命名空间中,所以如果你没有using boost::begin;提供你自己的东西,那么它永远不会通过非限定查找找到using boost::begin; 在你自己的范围内。

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

所以你的答案是:

对于Boost,不用担心它(如果需要,请升级Boost)。

对于新代码,请不要在任何定义类型的相同名称空间中定义名为begin的免费函数。

链接地址: http://www.djcxy.com/p/72939.html

上一篇: ADL and container functions (begin, end, etc)

下一篇: Implementing the swap in the copy and swap idiom