2012-06-18 20 views
24

Estoy desarrollando un proyecto que funciona con múltiples tipos aritméticos. Así que hice una cabecera, donde se definen los requisitos mínimos para un tipo definido por el usuario aritmética:¿Por qué algunas funciones en <cmath> no están en el espacio de nombres estándar?

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

Lo que me está preocupando es que cuando se utiliza un código como éste:

#include "user_defined_arithmetic.h" 

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

me sale un error de compilación:

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

Nunca he usado el encabezado <math.h>, solo el <cmath>. Nunca utilicé el using namespace std en un archivo de encabezado.

Estoy usando gcc 4.6. *. Comprobé lo que es la cabecera que contiene la declaración ambigua y resulta ser:

mathcalls.h:

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

que sé, que incluye <cmath><math.h>, pero debe proteger a las declaraciones de la espacio de nombres estándar Cavo en la cabecera <cmath> y encuentro:

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

lo tanto, el espacio de nombres std comienza después la #include <math.h>. ¿Hay algo mal aquí o malinterpreté algo?

+2

Algunas cosas que usted podría querer reconsiderar: cuando se utilizan tipos aritméticos (+ integrante tipos de matrimonio + float) por lo general es más eficiente (y común) para pasar por valor que por referencia. Cuando llame a una función para la cual desea una versión específica, califique la llamada, en lugar de agregar un 'using namespace X'. Alternativamente, puede usar una * directiva que usa * ('using arithmetic :: sin'). Finalmente, todo el enfoque de cambiar tipos editando un 'typedef' es una idea realmente mala. –

+0

@ DavidRodriguez-dribeas: ¡Gracias! Por favor, ¿podría darme una solución alternativa? Estoy usando pasar por referencia, porque el número puede ser un tipo personalizado. Eso significa que puede obtener incluso algunos kilobytes de gran tamaño. Tenía la esperanza de que cuando alinee las funciones y utilice las funciones básicas estándar dentro de la línea, no se haga daño. ¿O lo será? ¿Podría darme alguna sugerencia, por favor? –

+0

@ DavidRodriguez-dribeas: Sé que el enfoque C++ sería declarar una clase abstracta, pero una biblioteca que uso para cálculos matriciales usa optimizaciones significativas cuando usa un tipo incorporado. Simplemente no quería perder esta ventaja –

Respuesta

16

Las implementaciones de la biblioteca estándar de C++ pueden declarar las funciones de la biblioteca C en el espacio de nombres global así como en std. Algunos llamarían a esto un error, ya que (como has descubierto) la contaminación del espacio de nombres puede causar conflictos con tus propios nombres. Sin embargo, así son las cosas, así que debemos vivir con eso. Simplemente tendrá que calificar su nombre como arithmetic::sin.

En palabras de la norma (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

Si realmente quería, siempre se puede escribir un pequeño envoltorio alrededor de cmath, a lo largo de las líneas de:

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

Supongo que podría haber una sobrecarga de la llamada a la función adicional, dependiendo de qué tan inteligente sea el compilador.

+0

Esto no resuelve completamente el problema, considere 'namespace mylib { double sen (double x) { return 1.0; } } int main() { \t usando namespace mylib; std :: cout << stdmath :: sin (1) << std :: endl; std :: cout << sin (1) << std :: endl; } 'aún obtiene un" error de llamada ambigua ". – alfC

+0

@alfC no, no. El objetivo de esta respuesta es incluir '' en el archivo 'cpp' de' stdmath' en lugar del encabezado 'hpp'. – Ruslan

1

Esto es solo un humilde intento de comenzar a resolver este problema. (Las sugerencias son bienvenidas.)

He estado lidiando con este problema por mucho tiempo. Un caso fue el problema es muy evidente es este 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; 
} 

Esto es en mi opinión es un molesto error o por lo menos una situación muy desafortunada.(Al menos en GCC, y clang --utilizando la biblioteca GCC-- en Linux.)

Últimamente le di otra oportunidad al problema. Al mirar el cmath (de GCC) parece que el encabezado está allí simplemente para sobrecargar las funciones C y arruinar el espacio de nombres en el proceso.

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

Con que esto funciona

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

estoy casi seguro de que esto no es exactamente equivalente a #include<cmath> pero la mayoría de las funciones parecen funcionar.

Lo peor de todo es que finalmente alguna biblioteca de dependencia eventualmente lo hará #inclulde<cmath>. Por eso no pude encontrar una solución todavía.

NOTA: Huelga decir que esto no funciona en todos los

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

La declaración de cualquier cosa en 'namespace std' es UB. – Ruslan

Cuestiones relacionadas