【问题标题】:c++ using declaration and function overloadc++ 使用声明和函数重载
【发布时间】:2015-11-22 19:31:32
【问题描述】:
  1 #include <iostream>
  2 
  3 namespace primerlib {
  4     void compute() { std::cout << "primerlib::print()" << std:: endl; }
  5     void compute(const void *p) { std::cout << "primerlib::print(const void *p)" << std:: endl; }
  6 }
  7 
  8 //using primerlib::compute;
  9 //using namespace primerlib;
 10 void compute(int a) { std::cout << "::compute(int)" << std:: endl; }
 11 void compute(double d, double dd=3.4) { std::cout << "primerlib::compute(d, d)" << std:: endl; }
 12 void compute(char* p, char* p1 = 0) { std::cout << "primerlib::compute(char*, char*)" << std:: endl; }
 13 
 14 int main(void)
 15 {
 16     using primerlib::compute;
 17     //using namespace primerlib;
 18     compute(0);
 19     return 0;
 20 }

输出:

primerlib::print(const void *p)

我的问题是为什么它没有调用 global compute(int) one ?如果我在第 17 行使用 using 指令,它将调用 compute(int) 之一。非常感谢您的帮助。

【问题讨论】:

  • 这说明了为什么不应该以这种方式使用函数重载。不要仅仅因为您可以!而给程序中的每个函数都赋予相同的名称
  • 无论如何,using 并不总是指命名空间;它也可以指个别功能。这就是这个程序所做的。有人告诉它在primerlib 命名空间中使用compute 函数。我不确定我可以解释为什么当您取消注释第 17 行时它的行为会有所不同。 0 仍然是一个 const void 指针。
  • 这只是一个示例代码,用于理解命名空间、使用声明/指令和函数重载。
  • 您是否对如何使用0 调用void(const void*) 感到困惑? 0 代表一个空指针。
  • 不,我很困惑为什么不调用全局计算(int)。

标签: c++


【解决方案1】:

这是因为 using namespace Xusing X::Y 的工作方式不同。当您使用using namespace X 时,在名称查找期间会考虑该命名空间中的所有内容。考虑以下示例:

namespace A
{
    void foo(int){}
}

namespace B
{
    void foo(int){}
}

using namespace A;
using namespace B;

int main()
{
    foo(1);
}

在这里,AB 成员都将在名称查找期间被考虑,并且您将遇到 call of overloaded foo(int) is ambiguous 错误,因为编译器无法决定使用这两个函数中的哪一个,因为它们是相同的. using X::Y 语法旨在解决这个问题。当你使用它时,你告诉编译器只使用在X 命名空间中找到的Y,而不考虑其他任何东西。让我们将它添加到上面的示例中:

namespace A
{
    void foo(int){}
}

namespace B
{
    void foo(int){}
}

using namespace A;
using namespace B;


int main()
{
    using A::foo;  
    foo(1);
}

这里我们告诉编译器使用在A 命名空间中找到的foo 的实例,并忽略范围内的任何其他foos。因此,在您的示例中,您告诉编译器仅使用primerlib::compute,如果您想从全局范围访问函数,则必须使用::compute(),并调用compute(int)

【讨论】:

  • "当您使用 using namespace X,"时,该命名空间中的所有内容都会添加到包含 X 和当前范围的最近范围。名称不会添加到当前作用域,这是 using X::thing 所做的。
  • @Potatoswatter,抱歉,措辞不好。我的意思是在名称查找过程中会考虑它们。我会改写。
【解决方案2】:

使用primerlib::compute;

这是一个using 声明。当您使用它时,就好像在 using 声明的位置声明了一个名称。您可以通过添加 using ::foo; 来恢复全局 foo(或使用全局范围 ::foo(0); 调用它,您将在输出中看到 ::compute(int)

事实上,在您的示例编译器中没有找到全局 foo 声明,因为这是名称查找的工作方式 - 一旦找到名称,它就会搜索封闭范围和停止点。

有关使用声明的更多参考,请参阅:http://en.cppreference.com/w/cpp/language/namespace#Using-declarations

[编辑]

我误解了你的问题,在第 17 行你有“使用指令”,它的行为与 using declaration 不同,它不会在最近的封闭声明区域中引入名称(在你的情况下是它的主要功能),但将名称添加到最近的封闭命名空间 - 在这个演员表中,它是全局命名空间。更多参考:http://en.cppreference.com/w/cpp/language/namespace#Using-directives:

仅在命名空间范围和块范围内允许使用指令。从使用指令之后任何名称的非限定名称查找的角度来看,直到它出现的范围结束,namespace-name 中的每个名称都是可见的,就好像它在最近的封闭命名空间,其中包含使用指令和命名空间名称。

Using-directive 不会将任何名称添加到它出现的声明区域(与 using-declaration 不同),因此不会阻止相同的名称出现声明。

【讨论】:

  • 我的意思是全局重载对 main 也是可见的,为什么那些全局重载不被视为候选?此外,如果我使用第 17 行,它将调用全局。
  • @ponypaver 因为全局重载被using 遮蔽。 using ns::symbol 或多或少说“symbol 拉入本地范围”。由于compute() 现在可以在本地范围内找到,因此甚至不再搜索全局范围。
  • @ponypaver I mean the global overloads are also visible for main, 名称查找已经找到在函数范围内本地声明的名称,因此它不会查看全局范围,这就是为什么调用 compute(0.1);会产生错误。如果您在全局范围内使用 ADL(参数相关查找)有一种解决方法:class X{}; void compute(X n){},并且您在 main 中调用它:compute(X());,那么即使您有 using primerlib::compute;,它仍然会被发现;
【解决方案3】:

使用实际的 using 声明,您是说 compute 是一个名称,仅引用命名空间primerlib 中定义的函数:事实上,如果您尝试调用接受双精度的计算函数,您会收到没有匹配函数的错误. 使用注释的 using 声明,您赋予所有计算函数“相同的重要性”:要引用全局函数,您必须使用“::”命名空间解析指令,要引用 Primer 的命名空间指令,您必须使用“primerlib ::" 命名空间解析指令。

【讨论】:

  • 使用primerlib::compute和使用命名空间primerlib有什么区别?对我来说,我认为全局重载函数对 main 也是可见的。
【解决方案4】:

using directiveusing declaration 在这种情况下表现不同。

7.3.4$2 使用指令 [namespace.udir]:(我加粗)

using 指令指定指定命名空间中的名称 可以在 using 指令出现的范围内使用 使用指令。在非限定名称查找 (3.4.1) 期间,名称 看起来好像它们是在最近的封闭命名空间中声明的 其中包含 using 指令和指定的命名空间。 [ 注:在此上下文中,“包含”是指“直接包含或 间接”。 ——尾注]

在这种情况下,最近的封闭命名空间是全局命名空间,命名空间primerlib的函数与其他全局函数出现在同一个命名空间中,因此在调用重载决议::compute(int)之后。

7.3.3$1 使用声明 [namespace.udecl]:

using-declaration 将名称引入到声明区域 使用声明出现的地方。

namespaceprimerlib的函数会被引入main函数作用域,在名称查找的高优先级阶段被选中,然后停止名称查找。重载决议不会考虑全局函数,因此调用了primerlib::compute(const void *p)

顺便说一句:如果将using primerlib::compute; 移出main 函数,您将得到与在main 函数中使用using namespace primerlib; 相同的结果。

LIVE

【讨论】:

  • 那么为什么不选择全局重载作为调用候选,因为它们对 main 可见?
  • @ponypaver 在名称查找阶段,如果编译器可以在当前范围内找到名称(本例中为compute),则名称循环将停止。这意味着全局版本函数将不会被考虑用于重载决议。这是一种隐藏的名字。
  • 您必须在“最近的封闭命名空间”之后继续阅读。相关的命名空间不仅仅是封闭的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-23
  • 1970-01-01
  • 2023-03-23
  • 2017-07-12
  • 1970-01-01
相关资源
最近更新 更多