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

Почему некоторые функции в <cmath> не входят в пространство имен std?

Я разрабатываю проект, который работает с несколькими арифметическими типами. Поэтому я создал заголовок, где определены минимальные требования для определяемого пользователем арифметического типа:

user_defined_arithmetic.h:

typedef double ArithmeticF;   // The user chooses what type he 
                              // wants to use to represent a real number

namespace arithmetic          // and defines the functions related to that type
{

const ArithmeticF sin(const ArithmeticF& x);
const ArithmeticF cos(const ArithmeticF& x);
const ArithmeticF tan(const ArithmeticF& x);
...
}

Меня беспокоит то, что когда я использую такой код:

#include "user_defined_arithmetic.h"

void some_function()
{
    using namespace arithmetic;
    ArithmeticF lala(3);
    sin(lala);
}

Я получаю ошибку компилятора:

error: call of overloaded 'sin(ArithmeticF&)' is ambiguous
candidates are:
double sin(double)
const ArithmeticF arithmetic::sin(const ArithmeticF&)

Я никогда не использовал заголовок <math.h>, только <cmath>. Я никогда не использовал using namespace std в файле заголовка.

Я использую gcc 4.6. *. Я проверил, что представляет собой заголовок, содержащий двусмысленное объявление, и он оказывается:

mathcalls.h:

Prototype declarations for math functions; helper file for <math.h>.
...

Я знаю, что <cmath> включает <math.h>, но он должен защищать объявления пространством имен std. Я вникаю в заголовок <cmath> и нахожу:

cmath.h:

...

#include <math.h>

...

// Get rid of those macros defined in <math.h> in lieu of real functions.
#undef abs
#undef div
#undef acos
...

namespace std _GLIBCXX_VISIBILITY(default)
{
...

Итак, пространство имен std начинается после #include <math.h>. Здесь что-то не так, или я что-то не понял?

4b9b3361

Ответ 1

Реализации стандартной библиотеки С++ разрешено объявлять функции библиотеки C в глобальном пространстве имен, а также в std. Некоторые назвали бы это ошибкой, поскольку (как вы нашли) загрязнение пространства имен может вызвать конфликты с вашими собственными именами. Однако, так оно и есть, поэтому мы должны жить с ним. Вам просто нужно определить свое имя как arithmetic::sin.

В словах стандарта (С++ 11 17.6.1.2/4):

Однако в стандартной библиотеке С++ объявления (кроме имена, которые определены как макросы в C) находятся в области пространства имен (3.3.6) пространства имен std. Это не указано, объявлены ли эти имена сначала в области глобального пространства имен, а затем вставляются в пространство имен std с помощью явных использования-деклараций (7.3.3).

Ответ 2

Если вы действительно этого захотите, вы всегда можете написать небольшую обертку вокруг cmath по строкам:

//stdmath.cpp
#include <cmath>
namespace stdmath
{
    double sin(double x)
    {
        return std::sin(x);
    }
}

//stdmath.hpp
#ifndef STDMATH_HPP
#define STDMATH_HPP
namespace stdmath {
    double sin(double);
}
#endif

//uses_stdmath.cpp
#include <iostream>
#include "stdmath.hpp"

double sin(double x)
{
    return 1.0;
}

int main()
{
    std::cout << stdmath::sin(1) << std::endl;
    std::cout << sin(1) << std::endl;
}

Я полагаю, что из дополнительного вызова функции могут быть некоторые накладные расходы, в зависимости от того, насколько умным является компилятор.

Ответ 3

Это всего лишь скромная попытка начать решение этой проблемы. (Предложения приветствуются.)

Я давно занимаюсь этой проблемой. Дело в том, что проблема очень очевидна в этом случае:

#include<cmath>
#include<iostream>

namespace mylib{
    std::string exp(double x){return "mylib::exp";}
}

int main(){
    std::cout << std::exp(1.) << std::endl; // works
    std::cout << mylib::exp(1.) << std::endl; // works

    using namespace mylib;
    std::cout << exp(1.) << std::endl; //doesn't works!, "ambiguous" call
    return 0;
}

Это, на мой взгляд, раздражающая ошибка или, по крайней мере, очень неудачная ситуация. (По крайней мере, в GCC и clang - использование библиотеки GCC - в Linux.)

В последнее время я дал еще один шанс на проблему. Если посмотреть на cmath (GCC), кажется, что заголовок просто перегружает C-функции и закручивает пространство имен в процессе.

namespace std{
   #include<math.h>
}
//instead of #include<cmath>

С ним это работает

using namespace mylib;
std::cout << exp(1.) << std::endl; //now works.

Я почти уверен, что это не совсем эквивалентно #include<cmath>, но большинство функций, похоже, работают.

Хуже всего то, что в конечном итоге некоторая библиотека зависимости в конечном итоге будет #inclulde<cmath>. Для этого я еще не смог найти решение.

ПРИМЕЧАНИЕ: Нет необходимости говорить, что это вообще не работает

namespace std{
   #include<cmath> // compile errors
}