Подтвердить что ты не робот

Как предотвратить неявное использование double → int?

Вопрос, как указано выше, более подробно ниже:

У меня есть класс Money для решения... ну, вы догадались, что. Я очень строг в том, что не позволяю Money и double взаимодействовать (*), поэтому следующий код не:

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

Теперь я думаю о разрешении умножения Money на int, как в "у вас есть 6 кусочков торта за 4,50 доллара каждый (так что идите и найдите дешевый торт где-нибудь)".

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

Это прекрасно работает, , но. К сожалению, С++ делает неявные отбрасывания от double до int, поэтому мой первый фрагмент кода будет скомпилирован. Я не хочу этого. Есть ли способ предотвратить неявные приведения в этой ситуации?

Спасибо! - Робин

(*) Причина: у меня много устаревшего кода, который обрабатывает все Money -связанные вещи с помощью double, и я не хочу, чтобы эти типы путались до тех пор, пока все не будет запущено с Money.

Изменить: добавлены конструкторы для денег.

Изменить: Спасибо, всем, за ваши ответы. Почти все они были большими и полезными. Комментарий R. Martinho Fernandes "вы можете сделать inline const Money operator*( const Money & m, double d ) = delete;" на самом деле был ответом (как только я переключусь на поддерживающий С++ 11 компилятор). Kerrek SB дал хорошую альтернативу не-С++ 11, но то, что я в конечном итоге использовал, - это подход Николаса Мусатти "перегрузка long". Вот почему я отмечаю его ответ как "ответ" (также потому, что все полезные идеи появились в виде комментариев к его ответу). Еще раз спасибо!

4b9b3361

Ответ 1

Вы можете добавить объявление для личной перегрузки вашего расширенного оператора присваивания:

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

Ответ 2

Как насчет проверки шаблона плюс времени компиляции:

#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!");
  // ...
}

Ответ 3

Я могу представить два способа обеспечить это:

  • либо с использованием шаблонов и "концептуальных" проверок.
  • или с использованием "запрещенных" перегрузок

Использование перегрузок - это решение С++ 11. С++ 11 представляет ключевое слово delete, особенно для вашего случая!

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;

Старый способ (С++ 03) для этого был двойной:

  • в классе, объявите метод private
  • не определяют метод и ждут, пока компоновщик будет жаловаться

Вторая является защитой в случае метода класса и единственным способом в случае с свободным методом. Печально, что он обнаруживается только во время соединения... и ключевое слово delete намного приятнее;)


Использование шаблона - другое решение. Вы можете использовать либо std::enable_if, либо static_assert: один из них удалит функцию из набора перегрузки (SFINAE), в то время как другой сделает ошибку создания экземпляра (ошибка компилятора).

Пример:

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

Примеры для static_assert более естественны (это действительно как регулярное утверждение, действительно).


Я бы предпочел рекомендовать перегрузку + delete, если она у вас есть. Если вы этого не сделаете, то резервное копирование в случае с шаблоном, вероятно, является лучшим решением, поскольку легче исправить ошибки компилятора, чем ссылки.

Ответ 4

вы можете создать небольшой тип держателя номера, который имел все необходимые свойства, а затем использовать его для взаимодействия с другими типами, например Money.