2011-10-26 9 views
18

Pytanie jak wyżej, więcej szczegółów poniżej:Jak zapobiec niejawnym rzutowaniem podwójnym -> int?

Mam klasę Money do czynienia z ... cóż, zgadłeś co. Jestem bardzo o nie pozwalając Money i double interakcję (*), więc następujący kod jest nie możliwe:

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

Teraz myślę o umożliwiając namnażanie Money z int, jak w „Ci Mają 6 kawałków ciasta za 4,50 $ za sztukę (więc idź i znajdź gdzieś taniej ciasto). "

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

To działa dobrze, ale... niestety, C++ robi ukryte odlewane od double do int, tak nagle mój pierwszy fragment kodu zostanie skompilowany. Nie chcę tego. Czy istnieje sposób, aby w takich sytuacjach zapobiec domyślnym rzutom ?

Dzięki! - Robin

(*) Powód: Mam wiele jest starszych kod, który obsługuje wszystkie rzeczy związane z kondensatorem Money z double, a ja nie chcę tych rodzajów mylić aż wszystko przebiega z Money.

Edytuj: Dodano konstruktorów dla pieniędzy.

Edytuj: Dzięki, wszyscy, za twoje odpowiedzi. Prawie wszyscy byli wspaniali i pomocni. Komentarz R. Martinho Fernandesa "możesz zrobić inline const Money operator*(const Money & m, double d) = delete;" był właściwie odpowiedzią (jak tylko przejdę na kompilator wspierający C++ 11). Kerrek SB dał dobrą alternatywę bez C++ 11, ale to, co ostatecznie wykorzystałem, to podejście "przeciążenia long" Nicoli Musatti. Właśnie dlatego oznaczam jego odpowiedź jako "odpowiedź" (również dlatego, że wszystkie przydatne pomysły pojawiły się jako komentarze do jego odpowiedzi). Jeszcze raz dzięki!

+1

Konstruktorzy Show Money – mloskot

+0

Zmieniłem twój odnośnik "C" na "C++" w tekście. – xanatos

+0

Ale, jak pomyślałem przez mloskot, może istnieć niejawna konwersja podwójna -> Pieniądze – xanatos

Odpowiedz

8

Można dodać deklarację do prywatnej przeciążenia swojej rozszerzonej operatora przypisania:

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

Albo nawet uczyń go szablonem z kontrolą 'is_same' lub' is_integral'! –

+0

Działa to tylko przy użyciu 'Money m * = d;'. Ale w jaki sposób ukryć operatory (zewnętrzne/globalne) 'inline const Operator pieniędzy * (const Money & m, double d)' i 'inline const Operator money * (double d, const Money & m)'. Nie ma mowy, aby ustawić "prywatny", prawda? – Robin

+5

@Robin: w C++ 11 możesz zrobić 'inline const Operator pieniędzy * (const Money & m, double d) = delete;'. Jeśli Twój kompilator obsługuje to, użyj go! –

15

Jak o szablonie oraz kompilacji czek cecha:

#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

Dzięki, to działa. Jedyny problem to: teraz dostaję błędy kompilatora, ale dostaję je w money.h, nie w miejscu, w którym używam operatorów odznaczonych. Więc 'delete' lub' enable_if' może być jeszcze lepszy. (Lub po prostu przejście na kompilator zgodny z C++ 11, jeśli nie dla reguł firmy ...) – Robin

+1

@Robin: gdy wystąpi błąd podczas tworzenia szablonu (co ma miejsce w tym przypadku), kompilator powinien podać stos instancji, a dolny wpis w stosie to plik i linia, w której operator został faktycznie wywołany. –

0

można utworzyć małą liczbę typ posiadacza, który ma wszystkie pożądane właściwości, a następnie użyj go do połączenia z innymi typami, takimi jak Money.

7

mogę myśleć o dwóch sposobów, aby zapewnić w ten sposób:

  • albo przy użyciu szablonu i "Concept" kontrole
  • lub za pomocą "zabronione" przeciąża

Korzystanie przeciążeń jest Rozwiązanie C++ 11. C++ 11 wprowadza słowo kluczowe delete szczególnie w twoim przypadku!

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; 

Stary sposób (C++ 03), aby go o to była podwójna:

  • w klasie, zadeklarować metodę private
  • nie definiują metody i czekać na linker do reklamacji

Drugi to zabezpieczenie w przypadku metody klasowej i jedyny sposób w przypadku metody darmowej. To smutne, że jest on wykrywany tylko przez link czasie ... i kluczowe delete tylko dużo ładniejsze;)


Korzystanie szablon ma innego rozwiązania. Możesz użyć albo std::enable_if lub static_assert: jeden usunie funkcję z zestawu przeciążeniowego (SFINAE), podczas gdy druga spowoduje błąd instancji (błąd kompilatora).

Przykład:

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

Przykłady dla static_assert są bardziej naturalne (po prostu jak zwykły assert, naprawdę).


Wolałbym polecić przeciążenie + delete, jeśli masz. Jeśli tego nie zrobisz, prawdopodobnie najlepszym rozwiązaniem będzie powrót do szablonu, ponieważ łatwiej jest poprawić błędy kompilatora niż linker.

+0

Dzięki, zasadniczo podsumowałeś inne odpowiedzi, a także dodano "enable_if", który wygląda interesująco ("delete" nie działa z MSVC2010, 'static_assert' powoduje błąd kompilatora w Money, a nie w miejscu, w którym korzystam z operatora). Jednak nie mogę sprawić, że przykładowy kod 'enable_if' działa. Kod wygląda dobrze, więc obawiam się, że 'enable_if' to kolejna funkcja C++ 11, której MSVC2010 nie obsługuje ... – Robin

+0

@Robin: z którego' enable_if' korzystasz? FYI jest niewielka różnica między interfejsem 'std :: enable_if' i' boost :: enable_if', 'std :: enable_if' jest równoważne' boost :: enable_if_c'. Rozwiązanie Boost działa w C++ 03 i C++ 11, a rozwiązanie std po prostu je splądruje, ponieważ nie ma żadnej nowej funkcjonalności, powinno działać. –

Powiązane problemy