c++ - 将函数查找与 C++ 中的模板混淆

标签 c++ templates lookup

从以下内容开始(使用 gcc version 4.0.1):

namespace name {
   template <typename T>
   void foo(const T& t) {
      bar(t);
   }

   template <typename T>
   void bar(const T& t) {
      baz(t);
   }

   void baz(int) {
      std::cout << "baz(int)\n";
   }
}

如果我添加(在 global 命名空间中)

struct test {};
void bar(const test&) {
   std::cout << "bar(const test&)\n";
}

然后,如我所料,

name::foo(test()); // produces "bar(const test&)"

但如果我只是添加

void bar(const double&) {
   std::cout << "bar(const double&)\n";
}

它似乎找不到这个重载:

name::foo(5.0) // produces "baz(int)"

更重要的是,

typedef std::vector<int> Vec;
void bar(const Vec&) {
   std::cout << "bar(const Vec&)\n";
}

也没有出现,所以

name::foo(Vec());

编译错误

error: cannot convert ‘const std::vector<int, std::allocator<int> >’ to ‘int’ for argument ‘1’ to ‘void name::baz(int)’

这就是查找的工作方式吗? (注意:如果我删除命名空间 name,那么一切都会如我所料。)

如何修改此示例,以便考虑 bar 的任何重载? (我认为应该在 模板之前考虑重载?)

最佳答案

我假设您添加了 double version 也添加到全局命名空间,然后调用 foo在定义完所有内容后从 main 开始。所以这基本上是两个阶段的名称查找。查找因调用中的参数依赖(取决于其类型)而依赖的非限定函数名称分两个阶段完成。

第一阶段在定义上下文中进行非限定和参数相关的查找。然后它卡住结果,并使用实例化上下文(实例化点的声明的总和)进行第二个参数依赖查找。 No 不再进行不合格查找。因此,对于您的示例,它意味着:

  • 来电bar(t)foo<test> 内查找bar在实例化上下文中使用参数相关查找(它不会使用非限定查找找到它,因为 foo 被声明为 above 栏模板)。取决于你是否定义全局 barfoo 之前或之后模板,它将找到全局 bar在第一阶段已经使用参数依赖查找的声明(它在 test 的命名空间中定义)。然后 main 中的调用将实例化 foo<test>并且有可能找到 bar在这个阶段(如果你在声明模板之后声明它)。

  • 来电bar(t)foo<int> 内不进行参数相关的查找(或者更确切地说,查找的结果是一个空的声明集),因为 int是一个基本类型。因此,在定义上下文中的非限定查找也将一无所获,因为匹配 bar模板已声明 after foo模板。该调用格式错误,标准在 14.6.4.2/1 说明了这种情况。

    If the call would be ill-formed [...] then the program has undefined behavior.

    因此,您应该将此视为“我做了一件肮脏的事情,编译器选择不打我”的情况,我认为:)

  • 来电bar(t)foo<Vec> 内将再次进行查找,并将在 std:: 中查找 bar (因为那是定义 std::vector 的地方)。它没有找到 bar在那里,既不在定义上下文中。所以它决定再次使用未定义的行为,并使用 bar模板,并且它本身通过使用 baz 再次执行未定义的行为在它之后声明,ADL 或定义上下文中的非限定查找都找不到。

    如果 vector 是 vector<test> , 然后查找 bar也会在全局范围内完成,因为依赖于参数的查找不仅会直接使用参数类型,还会使用其中的模板参数类型(如果有的话)。


如果您使用 GCC,那么不要完全依赖它的行为。在下面的代码中,它声称调用不明确,尽管代码非常好 - fafake不应该是候选人。

namespace aname {
  struct A { };
  void f(A) { }
}

namespace afake {
  template<typename T>
  void g(T t) { f(t); }
  void f(aname::A) { }
}

int main() { aname::A a; afake::g(a); }

如果你想测试你的代码片段的一致性,最好使用 comeau online compiler与严格的设置。

关于c++ - 将函数查找与 C++ 中的模板混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1396458/

相关文章:

c++ - 在哪里定义具有模板和非模板成员的类?

python - 在python中存储一百万个键值对的列表

c++ - 为了在 C++ 中易于使用,使用引用而不是指针的合理性如何?

c++ - cudaDecodeGL sdk 示例从 Windows 移植到 Linux 后出现内存泄漏

html - 即使有条件评论,Outlook 中的表格宽度也会变大

C++ 模板 NULL 未声明的标识符

mongodb - 如何在 MongoDB 中标记文档?

java - 使用 InitialContext 进行 EJB 查找

c++ - 调用静态方法

c++ - 自动检测相同的连续 std::string::find() 调用