3

从以下开始(使用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";
   }
}

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

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)’

这是查找应该如何工作的方式吗?(注意:如果我删除了 namespace name,那么一切都会按我的预期进行。)

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

4

6 回答 6

8

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

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

  • 内部调用bar(t)在实例化上下文中使用参数相关foo<test>查找bar进行查找(它没有使用非限定查找找到它,因为foo它被声明above为 bar 模板)。根据您是bar在模板之前还是之后定义全局foo,它将bar在第一阶段已经使用参数依赖查找找到全局声明(它在test的命名空间中定义)。然后 main 中的调用将实例化foo<test>,并且可能会bar在此阶段找到(如果您在声明模板后声明了它)。

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

    如果调用格式不正确[...],则程序具有未定义的行为。

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

  • bar(t)内部调用foo<Vec>将再次进行查找,并将查找 bar in std::(因为这std::vector是定义的位置)。它在bar那里找不到,在定义上下文中也找不到。因此,它决定再次采用未定义的行为,并使用bar模板,而模板本身再次通过使用baz其后声明的声明来执行未定义的行为,并且 ADL 和定义上下文中的非限定查找都无法找到该模板。

    如果向量是 a vector<test>,那么查找bar也将在全局范围内完成,因为参数相关查找不仅会直接使用参数类型,还会使用其中的模板参数的类型(如果有的话)。


如果你使用 GCC,那么不要完全依赖它的行为。在下面的代码中,它声称调用是模棱两可的,尽管代码非常好 - finafake不应该是候选者。

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 在线编译器。

于 2009-09-09T21:50:13.600 回答
2

我可以确认您在我的系统上看到的行为,并且我相信它是正确的。

看起来重载决议只是在它的参数的名称空间中查找,所以它的版本bar需要一个test作品,因为test它在全局名称空间中,所以编译器在那里检查一个版本bar,正如你正确指出的那样,它的优先级高于模板版本。

对于Vec版本,重要的命名空间是std. 如果你把一个版本的barinstd你会发现它拿起它。

double版本不起作用,因为全局命名空间不用于查找,因为double它是一种内置类型,并且没有以任何方式与全局命名空间特别关联。

于 2009-09-08T22:14:28.643 回答
2

在“c++ koenig 查找”上进行谷歌搜索

这应该为您提供有关模板查找规则的足够信息。

Herb Sutter 有一篇关于这个主题的好文章:
http ://www.gotw.ca/gotw/030.htm

于 2009-09-08T23:02:28.450 回答
0

查找名称的规则是,如果名称不合格,则使用参数的命名空间来查找函数。

name::foo(test());之所以有效,是因为foo您基本上已经调用bar(test());并且测试的名称空间用于搜索栏。在这种情况下,全局命名空间。

name::foo(Vec());这不起作用,因为 Vec 是 typedef 而不是类或结构。

有关函数名称查找规则,请参见 C++ 标准。

于 2009-09-09T02:28:12.213 回答
-1

以下程序在 gcc 4.3 和 gcc 4.1 上运行良好(我手头仅有的两个编译器:

#include <iostream>
#include <vector>

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";
   }

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

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

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

int main()
{
    name::foo(name::test());
    name::foo(5.0);
    name::foo(name::Vec());
}

生产:

bar(const test&)
bar(const double&)
bar(const Vec&)

你用的是什么编译器?

于 2009-09-08T22:07:48.750 回答
-1

以下代码使用 VS 2005 专业版对我来说编译得很好:

#include <iostream>
#include <vector>

using std::cout;


typedef std::vector<int> Vec;

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";
    }   

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


int main()
{
    name::foo(Vec());
    return 0;
}

请发布您的原始代码,以便我们找出问题所在。

于 2009-09-08T22:11:55.840 回答