ADL没有按预期工作

时间:2017-08-14 13:27:08

标签: c++ iterator argument-dependent-lookup

struct S
{
    vector<int> v;
    void method()
    {
        begin(v);
    }
};

上面的代码段编译得很好,因为ADL直到我添加

auto begin() { return begin(v); }

到类声明。此时,C ++会忘记ADL,而是更喜欢甚至没有可行重载的S::begin,从而产生错误

  

error: no matching function for call to ‘S::begin(std::vector<int>&)’ begin(v);

有没有解决这个问题?我问,因为在阅读Why use non-member begin and end functions in C++11?之后,我开始在所有地方使用begin()end()个免费函数来保持一致性,但现在我在定义自己的begin()和{之后遇到了冲突{1}}方法。

2 个答案:

答案 0 :(得分:4)

正如评论S::begin中所提到的,隐藏std::begin。您可以通过键入std::begin或明确调用Susing std::begin带入std::begin的范围。

struct S
{
    std::vector<int> v;
    void method()
    {
        using std::begin;
        begin(v);
    }

    auto begin() {  using std::begin; return begin(v); }
};

答案 1 :(得分:2)

您使用begin作为非限定名称和非限定名称查找

  

...检查下面描述的范围,直到找到至少一个任何类型的声明,此时查找停止,不再检查其他范围。

reference

从您的成员函数的角度来看,提供名称begin的第一个范围是类范围,因此它会填充该范围的重载集,并停止查找。

只有在此名称查找阶段之后,它才会尝试选择其中一个重载集,并确定没有匹配项。编译器没有返回并从下一个范围开始搜索,它只是放弃了。

您的选择是:

  1. 使用现有的容器成员函数而不是自由函数(这比下面明确限定的版本略微冗长)

    auto begin() { return v.begin(); }
    
  2. 使用限定名称

    auto begin() { return ::std::begin(v); }
    
  3. 使用using std::begin;

    向类范围添加正确的重载

    忽略那一个,我忘了你不能在using的课堂范围内引入非会员名称。

  4. 将正确的重载注入到成员函数体本身的较窄范围内,因此搜索停在那里

    auto begin() { using std::begin; return begin(v); }
    
  5. 首先停止提供begin成员函数,而是将非成员begin重载添加到封闭名称空间。这是更现代的,可以避免查找问题。

    namespace N {
      struct S { std::vector<int> v; };
      std::vector<int>::iterator begin(S& s) { return s.v.begin(); }
      // and end, cbegin, etc. etc.
    }