2011-10-26 10 views
18

Pregunta como arriba, más detalles debajo:¿Cómo evito que un lanzamiento implícito doble -> int?

Tengo una clase Money para ocuparse de ... bueno, usted adivinó qué. Estoy muy estricta de no permitir Money y double para interactuar (*), por lo que el siguiente código es no posible:

Money m1(4.50); 
double d = 1.5; 
Money m2 = m1 * d; // <-- compiler error 

Ahora estoy pensando que permite la multiplicación de Money con int, como en "usted tiene 6 pedazos de pastel por $ 4.50 cada uno (así que ve y encuentra un pastel más barato en alguna parte) ".

class Money 
{ 
    Money(); 
    Money(const Money & other); 
    explicit Money(double d); 
    ... 
    Money & operator*=(int i); 
    ... 
} 
inline const Money operator*(const Money & m, int i) { return Money(m) *= i; } 
inline const Money operator*(int i, const Money & m) { return Money(m) *= i; } 

que funciona bien, pero ... por desgracia, C++ hace conversiones implícitas de double a int, tan de repente mi primer fragmento de código se compilará. No quiero eso. ¿Hay alguna manera de evitar moldes implícitos en esta situación?

Gracias! - Robin

(*) Motivo: Tengo mucho de la de código heredado que se encarga de todas las cosas Money -relacionado con double, y yo no quiero ese tipo confundido hasta que todo correr con Money.

Editar: Constructores agregados para Money.

Edit: Gracias, a todos, por sus respuestas. Casi todos fueron geniales y serviciales. El comentario de R. Martinho Fernandes "usted puede hacer inline const Money operator*(const Money & m, double d) = delete;" fue en realidad la respuesta (tan pronto como cambie a un compilador compatible con C++ 11). Kerrek SB dio una buena alternativa sin C++ 11, pero lo que terminé usando es en realidad el enfoque de "sobrecarga long" de Nicola Musatti. Es por eso que estoy marcando su respuesta como "la respuesta" (también porque todas las ideas útiles surgieron como comentarios a su respuesta). ¡Gracias otra véz!

+1

Mostrar constructores de dinero – mloskot

+0

He cambiado su referencia de 'C' a' C++ 'en el texto. – xanatos

+0

Pero, como lo pensó mloskot, podría haber una conversión implícita doble -> Dinero – xanatos

Respuesta

8

Se podría añadir una declaración para una sobrecarga privada de su operador de asignación aumentada:

private: 
    Money & operator*=(double i); 
+2

¡O incluso convertirlo en una plantilla con un cheque 'is_same' o' is_integral'! –

+0

Eso solo funciona para usar 'Money m * = d;'. Pero ¿cómo podría ocultar los operadores (externos/globales) en línea const Money operator * (const Money & m, double d) 'y' const Money const * (doble d, const Money & m) '. No hay manera de establecer 'private' allí, ¿verdad? – Robin

+5

@Robin: en C++ 11 puede hacer 'operador de dinero en línea const * (const Money & m, double d) = delete;'. Si su compilador lo admite, ¡úselo! –

15

¿Qué tal una plantilla de más tiempo de compilación cheque rasgo:

#include <type_traits> 

// ... 

template <typename T> 
Money & operator*=(const T & n) 
{ 
    static_assert(std::is_integral<T>::value, "Error: can only multiply money by integral amounts!"); 
    // ... 
} 
+0

Gracias, eso funciona. El único problema es: ahora obtengo errores de compilación, pero los obtengo en money.h, no donde uso los operadores forbitten. Entonces 'delete' o' enable_if' pueden ser incluso mejores. (O simplemente cambiando a un compilador compatible con C++ 11, si no fuera por las reglas de la compañía ...) – Robin

+1

@Robin: cuando se produce un error durante la creación de instancias de plantilla (que es el caso aquí), el compilador debe proporcionarle la pila de instanciación y la entrada inferior en la pila sería el archivo y la línea donde realmente se llamó al operador. –

0

Se puede crear un número pequeño tipo de soporte que tenía todas las propiedades que deseaba, luego use eso para interactuar con otros tipos, como Money.

7

puedo pensar en dos formas de proporcionar la siguiente:

  • ya sea usando la plantilla y los cheques "concepto"
  • o el uso de "prohibido" sobrecargas

El uso de sobrecargas es la C++ 11 solución. C++ 11 introduce la palabra clave delete especialmente para su caso!

Money& operator*=(int i); 
Money& operator*=(float f) = delete; 

Money operator*(Money m, int i) { return m*i; } 
Money operator*(Money m, float f) = delete; 

Money operator*(int i, Money m) { return m*i; } 
Money operator*(float f, Money m) = delete; 

La forma más antigua (C++ 03) para ir de eso era doble:

  • en la clase, declarar el método private
  • no definen el método y esperar a que el enlazador para quejarse

El segundo es una salvaguarda en el caso del método de clase, y la única forma en el caso del método libre. Es triste que sólo se ha detectado durante el enlace ... y la palabra clave delete es mucho más bonito;)


Utilizando la plantilla es otra solución. Puede usar std::enable_if o static_assert: uno eliminará la función del conjunto de sobrecarga (SFINAE) mientras que el otro hará que la creación de instancias falle (error de compilación).

Ejemplo:

// For enable_if 
template <typename T> 
std::enable_if<std::is_integral<T>::value, Money&> operator*=(T t); 

template <typename T> 
std::enable_if<std::is_integral<T>::value, Money> operator*(Money m, T t); 

template <typename T> 
std::enable_if<std::is_integral<T>::value, Money> operator*(T t, Money m); 

Los ejemplos de static_assert son más naturales (que al igual que una aserción regular, en realidad).


yo preferiría recomendar la sobrecarga + delete si lo tiene. Si no lo hace, entonces una alternativa al caso de la plantilla es probablemente la mejor solución, ya que es más fácil corregir los errores del compilador que los del enlazador.

+0

Gracias, básicamente resumió las otras respuestas, además agregó 'enable_if' que se ve interesante (' delete' no funciona con MSVC2010, 'static_assert' produce error de compilación en Money, no donde uso el operador). Sin embargo, no puedo obtener tu código para que funcione el ejemplo 'enable_if'. El código se ve bien, así que mi temor es que 'enable_if' sea otra característica de C++ 11 que MSVC2010 no admite ... – Robin

+0

@Robin: ¿qué' enable_if' estás usando? FYI hay una pequeña diferencia de interfaz entre 'std :: enable_if' y' boost :: enable_if', 'std :: enable_if' es equivalente a' boost :: enable_if_c'. La solución de Boost funciona en C++ 03 y C++ 11, y la solución estándar acaba de saquearlo, ya que no es necesaria ninguna nueva funcionalidad, debería funcionar. –

Cuestiones relacionadas