Может ли объявление повлиять на пространство имен std?

#include <iostream>
#include <cmath>

/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
return a > 0? -a : a;
}

int main() {
int a = abs(-5);
int b = std::abs(-5);
std::cout<< a << std::endl << b << std::endl;
return 0;
}

Я ожидал, что выходной будет -5а также 5, но на выходе -5 а также -5,

Интересно, почему это случится?

Имеет ли это какое-либо отношение к использованию std или что?

95

Решение

Спецификация языка позволяет реализации для реализации <cmath> путем объявления (и определения) стандартных функций в Глобальный пространство имен, а затем перенести их в пространство имен std с помощью объявлений об использовании. Не определено, используется ли этот подход

20.5.1.2 Заголовки
4 […] В стандартной библиотеке C ++, однако, объявления (за исключением имен, которые определены как макросы в C) находятся в пределах области имен (6.3.6) пространства имен std, Не указано, являются ли эти имена (включая любые перегрузки
добавленные в пунктах 21–33 и Приложении D) сначала объявляются в глобальной области имен, а затем внедряются в пространство имен std с помощью явных объявлений об использовании (10.3.3).

По-видимому, вы имеете дело с одной из реализаций, которая решила следовать этому подходу (например, GCC). То есть ваша реализация обеспечивает ::abs, в то время как std::abs просто «относится» к ::abs,

В этом случае остается один вопрос: почему в дополнение к стандарту? ::abs Вы смогли заявить о себе ::absто есть, почему нет ошибки множественного определения. Это может быть вызвано другой функцией, предоставляемой некоторыми реализациями (например, GCC): они объявляют стандартные функции как так называемые слабые символы, что позволяет вам «заменить» их собственными определениями.

Эти два фактора вместе создают эффект, который вы наблюдаете: замена слабых символов ::abs также приводит к замене std::abs, Насколько хорошо это согласуется с языковым стандартом, это отдельная история … В любом случае, не полагайтесь на это поведение — оно не гарантируется языком.

В GCC это поведение может быть воспроизведено на следующем минималистическом примере. Один исходный файл

#include <iostream>

void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }

Еще один исходный файл

#include <iostream>

void foo();
namespace N { using ::foo; }

void foo() { std::cout << "Goodbye!" << std::endl; }

int main()
{
foo();
N::foo();
}

В этом случае вы также заметите, что новое определение ::foo ("Goodbye!") во втором исходном файле также влияет на поведение N::foo, Оба вызова будут выводить "Goodbye!", И если вы удалите определение ::foo из второго исходного файла оба вызова будут отправлены в «оригинальное» определение ::foo и вывод "Hello!",


Разрешение, данное вышеупомянутым 20.5.1.2/4, предназначено для упрощения реализации <cmath>, Реализации разрешено просто включать в C-стиле <math.h>, а затем повторно объявить функции в std и добавить некоторые специфичные для C ++ дополнения и настройки. Если приведенное выше объяснение правильно описывает внутреннюю механику проблемы, то большая ее часть зависит от заменяемости слабых символов для Версии в стиле C функций.

Обратите внимание, что если мы просто глобально заменить int с double в приведенной выше программе код (под GCC) будет вести себя «как ожидалось» — он будет выводить -5 5, Это происходит потому, что стандартная библиотека C не имеет abs(double) функция. Объявив наш собственный abs(double)Мы ничего не заменим.

Но если после переключения с int с double мы также переключаемся с abs в fabs, оригинальное странное поведение вновь появится во всей красе -5 -5).

Это согласуется с приведенным выше объяснением.

90

Другие решения

Ваш код вызывает неопределенное поведение.

C ++ 17 [extern.names] / 4:

Каждая сигнатура функции из стандартной библиотеки C, объявленная с внешней связью, зарезервирована для реализации для использования в качестве сигнатуры функции как с внешней связью «C», так и с внешней связью «C ++», или в качестве имени области пространства имен в глобальном пространстве имен.

Таким образом, вы не можете создать функцию с тем же прототипом, что и стандартная библиотечная функция C. int abs(int);, Независимо от того, какие заголовки вы на самом деле включаете или эти заголовки также помещают имена библиотек C в глобальное пространство имен.

Тем не менее, было бы разрешено перегрузить abs если вы предоставляете разные типы параметров.

13

По вопросам рекламы [email protected]