Подтвердить что ты не робот

Почему мой журнал в пространстве имен 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;
}
4b9b3361

Ответ 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