Нет неоднозначной ошибки ссылки даже после использования директивы namespace
следующий код генерирует call of overloaded ‘bar()’ is ambiguous
ошибка, которая должна быть, поскольку у меня есть функция bar
как в глобальном, так и foo
пространство имен, и я назвал
5 ответов
в разделе C++ standard 17.6.1 содержание и организация библиотеки мы читаем в 17.6.1.2:
за исключением случаев, указанных в пунктах 18-30 И приложении D, содержание каждый заголовок cname должен быть таким же, как у соответствующего имя заголовка.h, как указано в стандартной библиотеке C (1.2) или C Unicode TR, по мере необходимости, как бы путем включения. В C ++ стандартная библиотека, однако, Объявления (за исключением имена, определенные как макросы в C), находятся в области пространства имен (3.3.6) пространства имен std. не указано, являются ли эти имена первыми объявляются в области глобального пространства имен и затем вводятся в пространство имен std с помощью явных объявлений using-(7.3.3).
курсив
кроме того, в 17.6.4.3.2 внешняя связь читаем
каждый имя из стандартной библиотеки C, объявленной с внешней связью зарезервирован для реализации для использования в качестве имени с extern " C" связь, как в пространстве имен std, так и в глобальном пространстве имен
на простом английском языке из этого раздела и подобных, имена стандартных библиотек C зарезервированы, но имена стандартных библиотек C находятся только в области глобального пространства имен.
что GLIBCXX делает здесь вполне допустимо; это объявлении abs
в глобальном пространстве имен объем и впрыскивать его в std
использование объявлений using -.
действительно, в стандартной библиотеке, которую используют мои system / 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
называет myabs
определена внутри main.cpp, а если вы комментируете myabs
на main.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
проблема в том, что <cstdlib>
- это действительно сложно из-за взаимодействия между заголовками C и заголовками c++. В libstdc++ он не реализован как:
namespace std {
int abs(int );
}
если это так, то ваш образец программы с std::abs
соответствовало бы вашему ожиданию о вашей программе образца с foo::bar
, по тем же причинам. Но вместо этого он объявляется как что-то вроде:
// from <stdlib.h>
extern int abs(int );
// from <cstdlib>
#include <stdlib.h>
namespace std {
using ::abs;
}
когда вы объявили и определили собственный ::abs(int )
, это просто повторное объявление ранее объявленного int ::abs(int )
. Вы ничего не перегружаете - есть только один int ::abs(int)
в этом блоке перевода! Вы могли бы увидеть это, если бы попытались объявить что-то вроде long abs(int )
- вы получите сообщение об ошибке redeclaration с другим типом возврата.
это работает, потому что ::abs
в заголовке C не определен (в противном случае вы получите ошибку компиляции при переопределении) - вы вводите это определение через общую библиотеку. И таким образом, вы заканчиваете с нарушением ODR, потому что у вас есть определение в TU и определение общей библиотеки в GLIBC, и, следовательно, неопределенное поведение. Я не знаю, почему линкер не поймал его.
если abs
функция объявляется следующим образом:
void abs(int n) {
return n > 0 ? n : -n;
}
(тип возврата изменен с int
to void
)
это позволит поднять error: ambiguating new declaration of 'void abs(int)'
потому что stdlib
он заявил как int abs(int n)
но мы определяем его сейчас с другим типом возврата.
так почему же он не жалуется, когда я определяю его с правильным типом возврата?
прежде всего, реализация int abs(int k)
находится в скомпилированном форма (стандартная библиотека) не в исходной форме. Поэтому невозможно сказать (перед связыванием), есть ли int abs(int k)
уже определено или нет. Поэтому компилятор доволен объявлением в cstdlib
и определение в нашем предоставил источник. И когда он начинает связывать его только поиск функции, которая объявлена, но еще не определена (так что она может скопировать определение (предполагаемая связь со статической библиотекой)). Поэтому linker не будет искать другое определение int abs(int k)
. Наконец, наше данное определение включено в результирующий двоичный файл.
я заметил следующее внутри <cstdlib>
:
#ifndef __CORRECT_ISO_CPP_STDLIB_H_PROTO
inline long
abs(long __i) { return __builtin_labs(__i); }
//...
когда я попробую ваш пример, используя long
,
#include <cstdlib>
#include <iostream>
long abs(long n) {
return n > 0 ? n : -n;
}
using namespace std;
int main() {
long k;
cin >> k;
cout << abs(k) << endl;
}
я получаю ожидаемую ошибку:
error: call of overloaded 'abs(long int&)' is ambiguous
может быть, ваша реализация делает что-то подобное.
Давайте изменим этот код следующим образом:
#include <iostream>
#include <cstdlib>
int abs(int n) {
std::cout << "default abs\n";
return n > 0 ? n : -n;
}
//using namespace std;
int main() {
int k;
std::cin >> k;
std::cout << std::abs(k) << std::endl;
}
он по-прежнему будет называть ваш abs. Странно , да? Хорошо, на самом деле нет