tas*_*oor 10 c++ namespaces c++11
下面的代码生成call of overloaded ‘bar()’ is ambiguous
错误应该是因为我bar
在全局和foo
命名空间中都有一个函数,并且我调用了using namespace foo
指令.
namespace foo {
void bar() {}
}
void bar() {}
using namespace foo;
int main() {
bar();
}
Run Code Online (Sandbox Code Playgroud)
我也期待以下代码出现同样的错误:
#include <cstdlib>
#include <iostream>
int abs(int n) {
return n > 0 ? n : -n;
}
using namespace std;
int main() {
int k;
cin >> k;
cout << abs(k) << endl;
}
Run Code Online (Sandbox Code Playgroud)
我已经定义了一个int abs(int n)
像cstlib中存在的函数,我调用了using namespace std
指令.所以应该像第一个例子一样出现错误.但没有.
我的问题是编译器如何解决这种歧义?在这种情况下会调用哪个函数,我的还是std
一个?这里有UB吗?
更新:从评论和答案看来,不同的编译器似乎表现不同.那么这个行为是未定义的还是实现定义的?
我与测试它g++ 4.8.4
在Ubuntu 14.04
与-std=c++11
国旗.
[请注意,我确实理解这using namespace std
很糟糕,我的abs
功能并不比std
一个更好甚至更糟.我的问题不同.]
在C++标准部分17.6.1库内容和组织中,我们在17.6.1.2中读到:
除第18条至第30条和附件D中所述外,每个标题cname的内容应与C标准库(1.2)或C Unicode TR中指定的相应标题name.h的内容相同. ,好像通过包含.但是,在C++标准库中,声明(除了在C中定义为宏的名称除外)都在命名空间std的命名空间范围(3.3.6)内.未指定是否首先在全局命名空间范围内声明这些名称,然后通过显式using-declarations将其注入命名空间std(7.3.3).
重点补充
另外,在17.6.4.3.2中我们读到了外部链接
使用外部链接声明的标准C库中的每个名称都保留给实现,以用作名称空间std和全局名称空间中具有extern"C"链接的名称
在本节的简明英语中,类似的,C标准库名称是保留的,但C标准库名称仅在全局命名空间范围内.
GLIBCXX在这里做的是完全有效的; 它abs
在全局命名空间范围内std
声明并将其注入使用using-declarations.
实际上,在我的系统/ g ++ 4.8.5和6.3.0使用的标准库中(我在coliru上检查过6.3.0),<cstdlib>
看起来像这样:
// <stdlib.h>:
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
Run Code Online (Sandbox Code Playgroud)
// <cstdlib>
#include <stdlib.h>
namespace std
{
using ::abs;
}
Run Code Online (Sandbox Code Playgroud)
它是using ::abs
使std::abs
调用你的函数.
您违反了ODR,因为GLIBC是一个共享库,它还提供了一个实现int abs(int)
.
您没有得到abs(int)
"链接器错误的多重定义"可能是编译器中的一个错误; 如果他们警告这个未定义的行为会很好.
这个例子可以复制:
main.cpp中
#include <iostream>
int myabs(int);
namespace foo {
int myabs(int n) {
return ::myabs(n);
}
}
int myabs(int n) {
std::cout << "myabs inside main.cpp\n";
return n > 0 ? n : -n;
}
using namespace foo;
int main() {
int k = -1;
std::cout << foo::myabs(k) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
myabs.cpp
#include <iostream>
int myabs(int n) {
std::cout << "myabs inside myabs.cpp\n";
return n > 0 ? n : -n;
}
Run Code Online (Sandbox Code Playgroud)
然后在命令行上:
g++ -fPIC -c myabs.cpp
g++ -shared myabs.o -o libmyabs.so
g++ -L. main.cpp -lmyabs
Run Code Online (Sandbox Code Playgroud)
运行./a.out
调用main.cpp中myabs
定义的内容,而如果你在main.cpp中注释掉它,它会调用myabs.cpp中的那个myabs
如果您避免在全局命名空间中声明函数,则应该主要避免此问题.
对于你的例子,如果我们改为写:
#include <cstdlib>
#include <iostream>
namespace {
int abs(int n) {
return n > 0 ? n : -n;
}
}
using namespace std;
int main() {
int k;
cin >> k;
cout << abs(k) << endl;
}
Run Code Online (Sandbox Code Playgroud)
我们得到关于呼叫不明确的预期错误警告.但是,请注意,如果标准库abs
在全局命名空间中声明,则无法解决问题:
int main() {
int k;
cin >> k;
cout << ::abs(k) << endl;
}
Run Code Online (Sandbox Code Playgroud)
这似乎只是调用标准库版本.当然,通过避免可以避免这个问题using namespace std
问题是由于C头和C++头之间的交互,这<cstdlib>
真的很复杂.在libstdc ++中,它没有实现为:
namespace std {
int abs(int );
}
Run Code Online (Sandbox Code Playgroud)
如果是这种情况,那么您的示例程序std::abs
将与您对样本程序的期望相匹配foo::bar
,原因完全相同.但相反,它被声明为:
// from <stdlib.h>
extern int abs(int );
// from <cstdlib>
#include <stdlib.h>
namespace std {
using ::abs;
}
Run Code Online (Sandbox Code Playgroud)
当您声明并定义自己的时::abs(int )
,这只是对先前声明的重新声明int ::abs(int )
.你没有超载任何东西 - int ::abs(int)
这个翻译单元只有一个!您可以看到,如果您尝试声明类似的内容long abs(int )
- 您将收到有关使用其他返回类型重新声明的错误.
这是有效的,因为::abs
在C头中没有定义(否则你会在重新定义时遇到编译错误) - 你通过共享库引入了该定义.因此,您最终会遇到ODR违规,因为您在TU中定义了您在GLIBC中的定义和共享库定义,因此未定义行为.我不确定为什么链接器不能捕获它.
归档时间: |
|
查看次数: |
935 次 |
最近记录: |