Почему мой журнал в пространстве имен std?
В приведенном ниже коде я определяю тривиальную функцию log
. В main
я стараюсь не называть его; Я звоню std::log
. Тем не менее, мой собственный log
называется; и я вижу "log!" на экране. Кто-нибудь знает, почему? Я использую g++ 4.7 и clang++ 3.2.
#include <iostream>
#include <cmath>
double log(const double x) { std::cout << "log!\n"; return x; }
int main(int argc, char *argv[])
{
std::log(3.14);
return 0;
}
Ответы
Ответ 1
С++ Стандарт 17.6.1.2, пункт 4 (основное внимание):
За исключением случаев, указанных в пунктах 18-30 и приложении D, содержимое каждого заголовка cname
должно быть таким же, как содержимое соответствующего заголовка name.h
, как указано в библиотеке C Standard (1.2) или в C Unicode TR, в зависимости от ситуации, как бы по включению. Однако в стандартной библиотеке С++ декларации (за исключением имен, которые определены как макросы в C) находятся в области пространства имен (3.3.6) пространства имен std
. Неизвестно, объявляются ли эти имена впервые в области глобального пространства имен и затем вводятся в пространство имен std
с помощью явных использования-деклараций (7.3.3).
g++ делает это последним, так что некоторые из тех же заголовочных файлов могут быть повторно использованы для C и С++. Поэтому g++ разрешено объявлять и определять double log(double)
в глобальном пространстве имен.
Раздел 17.6.4.3.3, пункты 3 и 4:
Каждое имя из библиотеки Standard C, объявленное с внешней связью, зарезервировано для реализации для использования в качестве имени с extern "C"
linkage, как в пространстве имен std
, так и в глобальном пространстве имен.
Каждая сигнатура функции из библиотеки Standard C, объявленная с внешней связью, зарезервирована для реализации для использования в качестве сигнатуры функции с привязкой extern "C"
и extern "C++"
или как имя области пространства имен в глобальном пространстве имен.
И вверху раздела 17.6.4.3 параграфа 2:
Если программа объявляет или определяет имя в контексте, где оно зарезервировано, кроме как явно разрешено этим разделом, его поведение undefined.
Вы, с другой стороны, не можете объявлять или определять ::log
каким-либо образом.
Слишком плохо, что g++ toolchain не дает вам никаких сообщений об ошибках.
Ответ 2
Что произойдет, я ожидаю, что std::log
просто делегирует ::log
. К сожалению, ::log
обеспечивает только перегрузку float
, и вы любезно предоставляете перегрузку double
, что делает ваше соответствие лучше. Но я до сих пор не вижу, как это даже рассматривается в наборе перегрузки.
Ответ 3
В libstdС++ cmath
вы увидите следующее:
using ::log;
Таким образом, он приводит функции math.h из глобального пространства имен в std
. К сожалению, вы поставляете реализацию для double log(double)
, поэтому компоновщик не будет использовать один из math lib. Так определенно ошибка в libstdС++.
EDIT: Я утверждаю, что это ошибка в libstdС++, потому что std::log
не должен страдать от помех в библиотеке C, когда вы явно запрашиваете версии std::
. Конечно, этот способ переопределить стандартные библиотечные функции - это старая "функция" на языке C.
ИЗМЕНИТЬ 2: Я узнал, что стандарт фактически не запрещает вносить имена из глобального пространства имен в std
. Так что не ошибка в конце концов, только следствие деталей реализации.
Ответ 4
В С++ компилятор может свободно реализовать библиотеку C в глобальном пространстве имен и делегировать ему (это определенная реализация).
17.6.1.2.4 За исключением случаев, указанных в пунктах 18-30 и приложении D, содержимое каждого заголовка cname должно быть одинаковым как и соответствующего заголовка name.h, как указано в стандартной библиотеке C (1.2) или в коде Unicode TR, в зависимости от ситуации, как бы по включению. Однако в стандартной библиотеке С++ объявления (кроме имена, которые определены как макросы в C) находятся в области пространства имен (3.3.6) пространства имен std. Это не указано, объявлены ли эти имена сначала в области глобального пространства имен и затем вставляются в пространство имен std с помощью явных использования-деклараций (7.3.3).
В общем, я бы избегал делать функцию с той же подписью, что и одна из стандартной библиотеки C. Стандарт С++, безусловно, дает компиляторам свободу использовать эти подписи, если он так выбирает, что означает, что вы можете бороться с вашим компилятором, если пытаетесь использовать те же подписи. Следовательно, вы получаете странные результаты.
Я бы ожидал ошибку компоновщика или предупреждение, хотя, думаю, может быть и стоит сообщить об этом.
[править]
Ничего себе, ниндзя.
Ответ 5
Потому что вы переопределили его в глобальном пространстве имен. Использование пространства имен позволяет избежать этой опасности, если вы не хотите переходить к более безопасному, более чистому языку, например Nim.
Правильное использование демонстрации пространства имен:
#include <iostream>
#include <cmath> // Uses ::log, which would be the log() here if it were not in a namespace, see http://stackoverflow.com/info/11892976/why-is-my-log-in-the-std-namespace
// Silently overrides std::log
//double log(double d) { return 420; }
namespace uniquename {
using namespace std; // So we don't have to waste space on std:: when not needed.
double log(double d) {
return 42;
}
int main() {
cout << "Our log: " << log(4.2) << endl;
cout << "Standard log: " << std::log(4.2);
return 0;
}
}
// Global wrapper for our contained code.
int main() {
return uniquename::main();
}
Вывод:
Our log: 42
Standard log: 1.43508