2012-06-18 13 views
24

Pracuję nad projektem, który działa z wieloma typami arytmetycznymi. Więc zrobiłem nagłówek, gdzie są określone minimalne wymagania dla zdefiniowanego przez użytkownika typu arytmetycznego:Dlaczego niektóre funkcje w <cmath> nie są w przestrzeni nazw standardu?

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); 
... 
} 

Co niepokoi mnie to, że podczas korzystania z kodu tak:

#include "user_defined_arithmetic.h" 

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

otrzymuję błąd kompilatora:

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

Nigdy nie użyłem nagłówka <math.h>, tylko <cmath>. Nigdy nie użyłem using namespace std w pliku nagłówkowym.

Używam gcc 4.6. *. Sprawdziłem co jest nagłówek zawierający niejednoznaczne oświadczenia i okazuje się, że:

mathcalls.h:

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

wiem, że <cmath> obejmuje <math.h>, ale powinna osłonić oświadczeń std przestrzeń nazw. Wbijam w nagłówku <cmath> i znaleźć:

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) 
{ 
... 

Więc std nazw zaczyna po#include <math.h>. Czy coś tu jest nie tak, czy źle coś zrozumiałem?

+2

niektóre rzeczy warto rozważyć: przy użyciu typów arytmetycznych (integralne typy + double + float) jest zwykle bardziej wydajne (i często), aby przejść przez wartość niż przez odniesienie. Podczas wywoływania funkcji, dla której chcesz konkretnej wersji, zakwalifikuj wywołanie, zamiast dodawać 'using namespace X'. Alternatywnie możesz użyć dyrektywy * używającej * ('using arithmetic :: sin'). Wreszcie całe podejście polegające na zmianie typów poprzez edycję 'typedef' jest naprawdę złym pomysłem. –

+0

@ DavidRodriguez-dribeas: Dziękujemy! Proszę, czy mógłbyś wskazać mi alternatywne rozwiązanie? Używam przejścia przez referencję, ponieważ liczba może być niestandardowym typem. Oznacza to, że może osiągnąć nawet kilka kilobajtów. Miałem nadzieję, że kiedy wprowadzę funkcje i użyję podstawowych funkcji elementarnych wewnątrz linii, nic złego się nie stanie. Lub będzie? Czy mógłbyś podać mi jakieś sugestie? –

+0

@ DavidRodriguez-dribeas: Wiem, że podejście C++ byłoby zadeklarować klasę abstrakcyjną, ale biblioteka, której używam do obliczeń macierzowych, używa znaczących optymalizacji, gdy używasz typu wbudowanego. Po prostu nie chciałem stracić tej przewagi. –

Odpowiedz

16

Implementacje biblioteki standardowej C++ mogą zadeklarować funkcje biblioteki C w globalnej przestrzeni nazw oraz w std. Niektórzy nazwaliby to błędem, ponieważ (jak już zauważyliście) zanieczyszczenie przestrzeni nazw może powodować konflikty z własnymi nazwiskami. Jednak tak właśnie jest, więc musimy z tym żyć. Musisz po prostu zakwalifikować swoje imię jako arithmetic::sin.

W słowach standard (C++ 11 17.6.1.2/4):

In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (3.3.6) of the namespace std . It is unspecified whether these names are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations (7.3.3).

3

Jeśli naprawdę chciał, zawsze można napisać trochę owijkę wokół cmath, wzdłuż linii:

//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; 
} 

Przypuszczam, że może być trochę głową z dodatkowego wywołania funkcji, w zależności od tego, jak mądry jest kompilator.

+0

To nie rozwiązuje całkowicie problemu, rozważ "namespace mylib { double sin (double x) { return 1.0; } } int main() { \t używanie przestrzeni nazw mylib; std :: cout << stdmath :: sin (1) << std :: endl; std :: cout << sin (1) << std :: endl; } 'nadal otrzymujesz" niejednoznaczny błąd połączenia ". – alfC

+0

@alfC Nie, nie. Cały punkt tej odpowiedzi polega na włączeniu '' w pliku 'cpp'' stdmath' zamiast nagłówka 'hpp'. – Ruslan

1

To tylko skromna próba rozpoczęcia rozwiązywania tego problemu. (Sugestie są mile widziane.)

Długo zajmowałem się tym problemem. Przypadek był problem jest bardzo oczywiste jest to przypadek:

#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; 
} 

Jest to moim zdaniem jest to irytujące bug lub co najmniej bardzo niefortunna sytuacja.(Przynajmniej w GCC i clang - przy użyciu biblioteki GCC - w Linuksie).

Ostatnio dałem mu kolejne zdjęcie tego problemu. Patrząc na cmath (z GCC) wydaje się, że nagłówek jest po prostu przeciążać funkcje C i przykręca przestrzeń nazw w procesie.

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

Z nim to działa

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

Jestem prawie pewien, że to nie jest dokładnie równoważne #include<cmath> ale większość funkcje wydają się działać.

Najgorsze ze wszystkiego jest to, że ostatecznie pewna biblioteka zależności w końcu będzie #inclulde<cmath>. Do tego jeszcze nie mogłem znaleźć rozwiązania.

UWAGA: Trzeba powiedzieć, to nie działa na wszystkich

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

Deklaracja czegokolwiek w 'namespace std' to UB. – Ruslan

Powiązane problemy