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

发布于 2024-08-04 09:17:40 字数 1312 浏览 3 评论 0原文

从以下内容开始(使用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 的任何重载? (我认为重载应该在模板之前被考虑?)

Starting with the following (using 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";
   }
}

If I add (in the global namespace)

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

then, as I expected,

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

But if I just add

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

it can't seem to find this overload:

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

What's more,

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

doesn't appear either, so

name::foo(Vec());

gives a compiler error

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

挽梦忆笙歌 2024-08-11 09:17:40

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

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

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

  • foo 中的调用 bar(t) 不会执行依赖于参数的查找(或者更确切地说,查找的结果是一个空声明集) ,因为 int 是基本类型。因此,对定义上下文的非限定查找也不会找到任何内容,因为匹配的 bar 模板是在 foo 模板之后声明的。该调用的格式不正确,标准在 14.6.4.2/1

    中描述了这种情况

    <块引用>

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

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

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

    如果向量是一个 vector,那么对 bar 的查找也将在全局范围内完成,因为依赖于参数的查找将不仅使用参数类型直接,还有其中模板参数的类型(如果有)。


如果您使用 GCC,那么不要完全依赖它的行为。在下面的代码中,它声称调用是不明确的,尽管代码完全没问题 - afake 中的 f 不应该是候选者。

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

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:

  • 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).

  • 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 :)

  • 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.

    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.


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

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

简单气质女生网名 2024-08-11 09:17:40

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

看起来重载解析只是在其参数的命名空间中查找,因此采用 testbar 版本可以工作,因为 test 位于全局命名空间,因此编译器会在那里检查 bar 的版本,正如您正确指出的那样,该版本优先于模板化版本。

对于 Vec 版本,重要的命名空间是 std。如果您将一个版本的 bar 放入 std 中,您会发现它会拾取它。

double 版本不起作用,因为全局命名空间不用于查找,因为 double 是内置类型,并且不以任何方式与全局命名空间专门关联。

I can confirm the behaviour you are seeing on my system and I believe it's correct.

Looks like the overload resolution is just looking in the namespaces of it's arguments so the version of bar that takes a test works because test is in the global namespace and so the compiler checks there for a version of bar which , as you rightly pointed out, is prioritised over the templated version.

For the Vec version the important namespace is std. If you put a version of bar in std you'll find it picks it up.

The double version doesn't work because the global namespace is not used for the lookup since double is a built-in type and not specially associated with the global namespace in any way.

逆流 2024-08-11 09:17:40

在“c++ koenig Lookup”上进行谷歌搜索,

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

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

Do a google on "c++ koenig lookup"

That should give you enough information on the template lookup rules.

Herb Sutter has a good article on the subject:
http://www.gotw.ca/gotw/030.htm

子栖 2024-08-11 09:17:40

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

name::foo(test()); 之所以有效,是因为在 foo 中你基本上调用了 bar(test()); 并且 test 的命名空间是用于搜索栏。在本例中,是全局命名空间。

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

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

The rule for looking up name is that if the name is unqualified, the parameter's namespace will be used to search for the function.

name::foo(test()); works because in foo you have call bar(test()); basically and test's namespace is used for searching bar. In this case, global namespace.

name::foo(Vec()); this wont work as Vec is a typedef not a class or struct.

See C++ standard for function name lookup rules.

醉南桥 2024-08-11 09:17:40

以下程序在 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&)
栏(const double&)
条(常量 Vec&)

您使用什么编译器?

The following program works fine for me on gcc 4.3 and gcc 4.1 (the only two compilers I have on hand:

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

Producing:

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

What compiler are you using?

你げ笑在眉眼 2024-08-11 09:17:40

下面的代码对我来说使用 VS 2005 Professional Edition 编译得很好:

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

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

The following code compiles fine for me using VS 2005 Professional Edition:

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

Please post your original code so that we can find out what's wrong.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文