Почему некоторые функции в <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>
. Здесь что-то не так, или я что-то не понял?
Ответы
Ответ 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
}