2012-06-18 14 views
24

Sto sviluppando un progetto che funziona con più tipi di aritmetica. Così ho fatto un colpo di testa, in cui sono definiti i requisiti minimi per un tipo aritmetico definito dall'utente:Perché alcune funzioni di <cmath> non si trovano nello spazio dei nomi 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); 
... 
} 

Quello che mi è preoccupante è che quando uso il codice come questo:

#include "user_defined_arithmetic.h" 

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

ottengo un errore di compilazione:

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

Non ho mai utilizzato l'intestazione <math.h>, solo lo <cmath>. Non ho mai usato lo using namespace std in un file di intestazione.

Sto utilizzando gcc 4.6. *. Ho controllato che cosa è l'intestazione contenente la dichiarazione ambigua e si scopre di essere:

mathcalls.h:

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

lo so, che include <cmath><math.h>, ma dovrebbe proteggere le dichiarazioni del spazio dei nomi std. I dig nell'intestazione <cmath> e trovo:

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

Così il namespace std inizia dopo il #include <math.h>. C'è qualcosa di sbagliato qui, o ho frainteso qualcosa?

+2

Alcune cose che si potrebbe voler riconsiderare: quando si utilizzano i tipi aritmetici (integrante tipi + doppia + float) di solito è più efficiente (e comune) per passare per valore che per riferimento. Quando si chiama una funzione per cui si desidera una versione specifica, si qualifica la chiamata, piuttosto che aggiungere un 'using namespace X'. In alternativa puoi usare un * usando direttiva * ('using arithmetic :: sin'). Infine, l'intero approccio alla modifica dei tipi modificando un typedef è una pessima idea. –

+0

@ DavidRodriguez-dribeas: Grazie! Per favore, potresti suggerirmi una soluzione alternativa? Sto usando il pass per riferimento, perché il numero può essere un tipo personalizzato. Ciò significa che può ottenere anche pochi kilobyte di grandi dimensioni. Speravo che quando inserirò le funzioni e usassi le funzioni elementari std all'interno della linea, non avremo alcun danno. O lo sarà? Potresti darmi qualche suggerimento, per favore? –

+0

@ DavidRodriguez-dribeas: So che l'approccio C++ sarebbe quello di dichiarare una classe astratta, ma una libreria che utilizzo per i calcoli a matrice utilizza significative ottimizzazioni quando si utilizza un tipo incorporato. Non volevo perdere questo vantaggio –

risposta

16

Le implementazioni della libreria standard C++ sono autorizzate a dichiarare funzioni di libreria C nello spazio dei nomi globale nonché in std. Alcuni lo chiamerebbero un errore, dal momento che (come hai trovato) l'inquinamento dello spazio dei nomi può causare conflitti con i tuoi stessi nomi. Tuttavia, è così com'è, quindi dobbiamo conviverci. Devi solo qualificare il tuo nome come arithmetic::sin.

Nelle parole dello 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

Se si voleva davvero, si può sempre scrivere un po 'wrapper cmath, lungo le linee di:

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

suppongo ci potrebbe essere un certo overhead dalla chiamata di funzione aggiuntiva, a seconda di come il compilatore intelligente è.

+0

Questo non risolve completamente il problema, si consideri 'namespace mylib { double sin (double x) { return 1.0; } } int main() { \t utilizzando namespace mylib; std :: cout << stdmath :: sin (1) << std :: endl; std :: cout << sin (1) << std :: endl; } 'ottieni ancora un" errore di chiamata ambiguo ". – alfC

+0

@alfC no non lo fai. Il punto centrale di questa risposta è l'inclusione di '' nel file 'cpp' di' stdmath' invece dell'intestazione 'hpp'. – Ruslan

1

Questo è solo un umile tentativo di iniziare a risolvere questo problema. (I suggerimenti sono i benvenuti.)

Ho avuto a che fare con questo problema da molto tempo. Un caso fosse il problema è molto evidente è questo caso:

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

Questo è a mio parere è un fastidioso bug o per lo meno una situazione molto spiacevole.(Almeno in GCC, e clang --utilizzando la libreria GCC-- in Linux.)

Ultimamente ho dato un altro colpo al problema. Guardando lo cmath (di GCC) sembra che l'intestazione ci sia semplicemente per sovraccaricare le funzioni C e rovinare lo spazio dei nomi nel processo.

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

Con esso questo funziona

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

Sono quasi sicuro che questo non è esattamente equivalente a #include<cmath> ma maggior parte delle funzioni sembra funzionare.

La cosa peggiore è che alla fine una libreria di dipendenze finirà per lo #inclulde<cmath>. Per quello non sono riuscito a trovare ancora una soluzione.

NOTA: Inutile dire che questo non funziona a tutti i

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

La dichiarazione di qualcosa in 'namespace std' è UB. – Ruslan

Problemi correlati