ADL如何影响这段C ++代码?

问题描述

实际上,以下代码无法使用Clang使用此命令进行编译:

clang++ -std=c++11 test.cc -o test

我只想模仿与C ++中的“交换惯用语”相同的行为,以使用“ using-directive”启用ADL。但是以下代码在哪里出错?预期的呼叫优先级应为:N1::foo> N2::foo> ::foo,对吧?

namespace N1 {
  struct S {};
  void foo(S s) {
    std::cout << "called N1::foo.";
  }
}
namespace N2 {
  void foo(N1::S s) {
    std::cout << "called N2::foo.";
  }
}
void foo(N1::S s) {
  std::cout << "called foo.";
}
int main() {
  using N2::foo;  
  foo(N1::S{});
}

错误消息:

test.cc:54:3: error: call to 'foo' is ambiguous
  foo(N1::S{});
  ^~~
test.cc:40:8: note: candidate function
  void foo(S s) {
       ^
test.cc:45:8: note: candidate function
  void foo(N1::S s) {
       ^
1 error generated.

已更新:

我将N2 :: foo更改为可以模仿std :: swap的模板方法。因此,这里的问题是,为什么::foo在函数foo(N1::S{});中不能被main调用?由于该函数比具有相同优先级的模板函数要合适得多。

namespace N1 {
  struct S {};
  /*
  void foo(S s) {
    std::cout << "called N1::foo,specific one." << '\n';
  }
  */
}
namespace N2 {  // as a fallback to unqualified name which has no user-defined overload.
  template<typename T>
  void foo(T) {
    std::cout << "called N2::foo,generic one." << '\n';
  }
}
void foo(N1::S s) {
  std::cout << "called foo." << '\n';
}
int main() {
  using N2::foo;
  foo(N1::S{});
  foo(10);  // use generic version.
}

解决方法

在这种情况下,normal name lookup找到N2::fooADL找到N1::foo,它们都被添加到重载集中,然后执行重载解析并通话不明确。

BTW:在using N2::foo;中没有main()的情况下,将通过普通名称查找找到::foo,并且也由ADL找到N1::foo;结果,通话仍然不明确。

已更新:

所以,这里的问题是,为什么::foo函数中的foo(N1::S{});无法被main调用?

由于使用using N2::foo;,因此N2::foo函数中引入了名称main。调用foo时,将在N2::foo的范围内找到名称main,然后名称查找停止,不会检查其他范围(全局名称空间),因此{{1 }}根本找不到并添加到过载集。结果,两种情况都调用::foo

名称查找按如下所述检查scopes,直到找到至少一个任何类型的声明为止,这时查找停止,并且不检查其他范围。

顺便说一句:如果将N2::foo放在using N2::foo;之前的全局名称空间中,则main将调用foo(N1::S{});::fooN2::foo均可通过名称查找找到,::foo在重载解析中胜出。

LIVE

,

首先,您具有一个普通的查找,该查找从内部范围搜索到外部范围,并在第一次匹配时停止,以隐藏更高版本范围的过载。然后,当触发ADL时,它将向搜索添加其他关联的实体和名称空间。

因此,在您的情况下,ADL查找不会向过载集添加任何内容。

#include <iostream>

namespace N1 {
  struct S {};
  /*
  void foo(S s) {
    std::cout << "called N1::foo,specific one." << '\n';
  }
  */
}
namespace N2 { 
  template<typename T>
  void foo(T) {
    std::cout << "called N2::foo,generic one." << '\n';
  }
}
void foo(N1::S s) {
  std::cout << "called foo." << '\n';
}
int main() {
  using N2::foo;
  foo(N1::S{}); // ordinary lookup finds N2 in main scope and stops.
                // adl lookup add N1 ns to the additionnal ns set but finds nothing
                // overload set = N2::foo by ordinary lookup
}

现在,如果我取消注释您的S1版本,那就赢了!这就是您得到的:

#include <iostream>

namespace N1 {
  struct S {};
  void foo(S s) {
    std::cout << "called N1::foo,specific one." << '\n';
  }
}
namespace N2 { 
  template<typename T>
  void foo(T) {
    std::cout << "called N2::foo,generic one." << '\n';
  }
}
void foo(N1::S s) {
  std::cout << "called foo." << '\n';
}
int main() {
  using N2::foo;
  foo(N1::S{}); // ordinary lookup finds N2 in main scope and stops
                // adl lookup add N1 ns to the additionnal ns set and finds N1::foo
                // overload set = N2::foo by ordinary lookup,N1::foo by ADL,N1::foo wins
}

通过swap获得相同的结果。在这种情况下,使用Foo :: swap,因为它位于N1命名空间中:

#include <iostream>

namespace N1 {
    struct Foo {

    };
    void swap(Foo&,Foo&) {
        std::cout << "swap Foo" << std::endl;
    }
}

namespace N2 {

struct S {
    N1::Foo f;
};

void swap(S& l,S& r) {
    using std::swap; // overload set is std::swap by ordinary lookup
    swap(l.f,r.f); // and Foo::swap by ADL,Foo::swap wins
}
}

int main() {
    N2::S s1,s2;
    swap(s1,s2);
}

但是,如果您在全局ns中移动Foo特定交换,则std::swap会被调用:

#include <iostream>

namespace N1 {
    struct Foo {

    };
}

void swap(N1::Foo&,N1::Foo&) {
    std::cout << "swap Foo" << std::endl;
}

namespace N2 {

struct S {
    N1::Foo f;
};

void swap(S& l,r.f); // because ADL does not add the global ns to the
                    // ns to be searched for
}
}

int main() {
    N2::S s1,s2);
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...