c++ - 即使在使用命名空间指令后也没有歧义引用错误

标签 c++ c++11 namespaces

下面的代码生成了 call of overloaded 'bar()' is ambiguous 错误,它应该是因为我在全局和 foo 中都有一个函数 bar 命名空间,我调用了 using namespace foo 指令。

namespace foo {
    void bar() {}
}

void bar() {}

using namespace foo;

int main() {
    bar();
}

我原以为下面的代码也会出现同样的错误:

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

我已经定义了一个函数 int abs(int n) 就像 cstlib 中的函数一样我调用了 using namespace std 指令。所以应该和第一个例子一样出现错误。但是没有。

我的问题是编译器是如何解决这种歧义的?在这种情况下会调用哪个函数,是我的函数还是 std 的函数?这里有UB吗?

更新:从评论和回答来看,不同的编译器似乎表现不同。那么这个行为是未定义的还是实现定义的?

我已经在带有 -std=c++11 标志的 Ubuntu 14.04 上用 g++ 4.8.4 测试了它。

[请注意,我明白 using namespace std 是不好的,我的 abs 功能并不比 std 更好甚至更差.我的问题不同。]

最佳答案

在 C++ 标准部分 17.6.1 Library contents and organization ,我们在17.6.1.2中读到:

Except as noted in Clauses 18 through 30 and Annex D, the contents of each header cname shall be the same as that of the corresponding header name.h , as specified in the C standard library (1.2) or the C Unicode TR, as appropriate, as if by inclusion. In the C ++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (3.3.6) of the namespace std. It is unspecified whether these names are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations (7.3.3).

已添加重点

此外,在 17.6.4.3.2 External linkage我们读

Each name from the Standard C library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace

在本节和类似部分的简单英语中,C 标准库名称是保留的,但 C 标准库名称仅在全局命名空间范围内。

GLIBCXX 在这里所做的是完全有效的;它正在声明 abs在全局命名空间范围内并将其注入(inject) std使用 using 声明。

确实,在我的系统/g++ 4.8.5 和 6.3.0 使用的标准库中(6.3.0 我在 coliru 上检查过),<cstdlib>看起来像这样:

// <stdlib.h>:

extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
// <cstdlib>

#include <stdlib.h>

namespace std
{
    using ::abs;
}

就是那个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;
}

myabs.cpp

#include <iostream>

int myabs(int n) {
    std::cout << "myabs inside myabs.cpp\n";
    return n > 0 ? n : -n;
}

然后在命令行上:

g++ -fPIC -c myabs.cpp
g++ -shared myabs.o -o libmyabs.so
g++ -L. main.cpp -lmyabs

正在运行 ./a.out调用 myabsma​​in.cpp 中定义,而如果您注释掉 myabsma​​in.cpp 中,它调用来自 myabs.cpp

的那个

如何避免这个问题

如果你避免在全局命名空间中声明函数,你应该主要避免这个问题。

对于您的示例,如果我们改写:

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

我们收到有关调用不明确的预期错误警告。但是,请注意,如果标准库声明 abs,这并不能解决问题。在全局命名空间中:

int main() {
    int k;

    cin >> k;

    cout << ::abs(k) << endl;
}

那好像只是调用了标准库版本。自然地,这个问题可以通过避免 using namespace std 来避免。

关于c++ - 即使在使用命名空间指令后也没有歧义引用错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43263880/

相关文章:

c++ - 创建动态二维数组的其他方法?

c++ - 如何实现方便的初始化?

c++ - 可变参数模板,完美转发到具有默认参数的函数

c++11 - 如何获取 C++ 的 Bazel 代码覆盖率摘要?

module - 不能使用 "super"来引用由 "use"从另一个 crate 引入的名称

C++,如何优化浮点算术运算?

c++ - 如何重新键入指向类的指针并能够根据其类型访问类函数?

PHP Web 服务器集成到本地运行的 C++ exe?他们如何互动?

android-studio - Android Studio 中带有格式错误的应用程序命名空间的麻烦 XML Font-Family 文件

namespaces - 在命名空间声明后立即添加 "using namespace"是否可以接受?