使用C ++中的模板进行混淆函数查找 [英] Confusing function lookup with templates in C++

查看:135
本文介绍了使用C ++中的模板进行混淆函数查找的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



如果我在 / em>命名空间)

If I add (in the global namespace)

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 ,那么一切都按照我的预期工作。)

Is this how the lookup is supposed to work? (Note: if I remove the namespace name, then everything works as I expected.)

这个例子使得 bar 的任何重载被考虑? (我认为重载应该在之前被考虑在内)

How can I modify this example so that any overload for bar is considered? (I thought that overloads were supposed to be considered before templates?)

推荐答案

double 版本到全局命名空间,并且在定义所有内容后从main调用 foo 。所以这基本上是两阶段名称查找。查找因为调用中的参数是依赖(取决于其类型)而依赖的不合格函数名称是分两个阶段完成的。

I assume you added the double version to the global namespace too, and you call foo from main after everything is defined. So this is basically two phase name lookup. Looking up an unqualified function name that is dependent because an argument in the call is dependent (on its type) is done in two phases.

第一阶段在定义上下文中进行非限定和参数依赖查找。然后它冻结结果,并使用实例化上下文(实例化时的声明的和)仅进行第二参数依赖查找。 无限制查找已完成。所以对于你的例子,它的意思是:

The first phase does a unqualified and argument dependent lookup in the definition context. It then freezes the result, and using the instantiation context (the sum of the declarations at the point of instantiation) does a second argument dependent lookup only. No unqualified lookup is done anymore. So for your example it means:


  • 调用 bar(t) foo< test> 在实例化上下文中查找 bar 使用不合格的查找,因为 foo 被声明为上面栏模板)。根据您是否在 foo 模板之前或之后定义全局 bar ,它将找到全局<$ c $ (在 test 的命名空间中定义)中使用参数依赖查找的c> bar 声明。然后main中的调用将实例化 foo< test> ,并且在这个阶段可能找到 bar 它在你声明模板后)。

  • The call bar(t) within foo<test> looks up bar using argument dependent lookup at the instantiation context (it doesn't find it using unqualified lookup, because foo is declared above the bar template). Depending on whether you define the global bar before or after the foo template, it will find the global bar declaration using argument dependent lookup already in the first phase (it's defined in test's namespace). Then the call in main will instantiate foo<test> and it will possible find bar in this phase (if you declared it after you declared the template).

bar(t) code>不执行参数依赖查找(或者更确切地说,查找的结果是一个空的声明集),因为 int 是一个基本类型。因此,在定义上下文中的无限定查找也找不到任何东西,因为匹配的 bar 模板被声明为之后 c $ c> foo 模板。调用将是不成形的,标准在 14.6.4.2/1

The call bar(t) within foo<int> doesn't do argument dependent lookup (or rather, the result for the lookup is an empty declaration set), because int is a fundamental type. So, unqualified lookup at the definition context will find nothing either, because the matching bar template is declared after the foo template. The call would be ill-formed, and the standard says about this situation at 14.6.4.2/1


如果调用不成功,程序就会有未定义的行为。

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

应该考虑这是一个我做了一个脏东西,编译器选择不打我的情况下,我想:)

You should therefor consider this as a "i did a dirty thing and the compiler chose not to slap me" case, i think :)

调用 bar(t)内,foo< Vec> 将再次执行查找, std :: (因为那里定义了 std :: vector )。它没有在定义上下文中找到 bar 。因此,它决定再次通过未定义的行为,并使用 bar 模板,并且其本身也通过使用 baz 在它之后声明,并且无法由定义上下文的ADL或非限定查找找到。

The call bar(t) within foo<Vec> will do the lookups again, and will look for bar in std:: (because that's where std::vector is defined). It doesn't find a bar there, neither in the definition context. So it decides to go by undefined behavior again, and uses the bar template, and which in itself again does undefined behavior by using the baz declared after it and which cannot be found by neither ADL nor unqualified lookup from the definition context.

如果向量是向量< test> ,则查找 bar 也将在全局范围内完成,因为参数依赖查找将不仅直接使用参数类型,而且还在其中包含模板参数的类型(如果有)。

If the vector were a vector<test>, then lookup for bar would be done at global scope too, because argument dependent lookup will not only use the argument type directly, but also the type of the template arguments in them, if there are any.


,那么不要完全依赖其行为。在下面的代码中,它声称调用是不明确的,虽然代码是完全正常的 f afake 应该不是候选人。

If you use GCC, then don't rely entirely on its behavior. In the following code, it claims the call is ambiguous, although the code is perfectly fine - the f in afake should not be a candidate.

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

如果你想测试你的代码片段是否符合,最好使用 gotau在线编译器与严格的设置。

If you want to test your snippets against conformance, best use the comeau online compiler with the strict settings.

这篇关于使用C ++中的模板进行混淆函数查找的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆