2011-10-26 29 views
18

Câu hỏi như trên, chi tiết hơn bên dưới:Làm thế nào để ngăn chặn một diễn viên tiềm ẩn đôi -> int?

Tôi có một lớp học Money để giải quyết ... tốt, bạn đã đoán được điều gì. Tôi rất nghiêm ngặt về việc không cho phép Moneydouble để tương tác (*), do đó đoạn mã sau là không thể:

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

Bây giờ tôi đang suy nghĩ về việc cho phép nhân của Money với int, như trong "bạn có 6 miếng bánh với giá $ 4,50 mỗi cái (nên đi tìm bánh rẻ hơn ở đâu đó). "

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

đó làm việc tốt, nhưng ... không may, C++ không phôi ngầm từ double để int, vì vậy đột nhiên đoạn mã đầu tiên của tôi sẽ biên dịch. Tôi không muốn điều đó. Có cách nào để ngăn chặn các phôi tiềm ẩn trong trường hợp này không?

Cảm ơn! - Robin

(*) Lý do: Tôi có nhiều mã di sản xử lý tất cả các công cụ có liên quan Money có liên quan double và tôi không muốn các loại đó nhầm lẫn cho đến khi mọi thứ chạy với Money.

Chỉnh sửa: Thêm hàm tạo cho tiền.

Chỉnh sửa: Cảm ơn mọi người, về câu trả lời của bạn. Hầu như tất cả chúng đều tuyệt vời và hữu ích. R. Martinho Fernandes 'bình luận' bạn có thể làm inline const Money operator*(const Money & m, double d) = delete; "thực sự là câu trả lời (ngay khi tôi chuyển sang trình biên dịch hỗ trợ C++ 11). Kerrek SB đã đưa ra một lựa chọn không thay thế C++ 11, nhưng những gì tôi đã kết thúc với việc sử dụng thực sự là phương pháp "quá tải long" của Nicola Musatti. Đó là lý do tại sao tôi đang gắn cờ câu trả lời của anh ấy là "câu trả lời" (cũng bởi vì tất cả các ý tưởng hữu ích đều được đưa ra làm bình luận cho câu trả lời của anh). Một lần nữa cám ơn!

+1

Hiển thị các nhà xây dựng tiền – mloskot

+0

Tôi đã thay đổi tham chiếu của bạn về 'C' thành' C++' trong văn bản. – xanatos

+0

Nhưng, theo suy nghĩ của mloskot, có thể có một chuyển đổi ngầm ẩn đôi -> Tiền – xanatos

Trả lời

8

Bạn có thể thêm một tuyên bố cho một tình trạng quá tải riêng của toán tử gán Augmented của bạn:

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

Hoặc thậm chí làm cho nó một mẫu với một 'is_same' hoặc' is_integral' kiểm tra! –

+0

Chỉ hoạt động khi sử dụng 'Tiền m * = d;'. Nhưng làm thế nào tôi sẽ ẩn (bên ngoài/toàn cầu) nhà điều hành 'inline const tiền nhà điều hành * (const Money & m, đôi d)' và 'inline const tiền nhà điều hành * (double d, const Money & m)'. Không có cách nào để đặt 'riêng tư' ở đó, phải không? – Robin

+5

@Robin: trong C++ 11 bạn có thể thực hiện 'inline const Money operator * (const Money & m, double d) = delete;'. Nếu trình biên dịch của bạn hỗ trợ điều này, hãy sử dụng nó! –

15

Làm thế nào về một mẫu cộng với thời gian biên dịch kiểm tra đặc điểm:

#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

Cảm ơn, điều đó hoạt động. Chỉ có vấn đề là: Bây giờ tôi nhận được lỗi trình biên dịch, nhưng tôi nhận được chúng trong money.h, không phải nơi tôi sử dụng các nhà khai thác forbitten. Vì vậy, 'xóa' hoặc' enable_if' có thể tốt hơn. (Hoặc chỉ cần chuyển sang trình biên dịch tương thích C++ 11, nếu không cho các quy tắc của công ty ...) – Robin

+1

@Robin: khi xảy ra lỗi trong quá trình tạo mẫu (trường hợp này) trình biên dịch sẽ cung cấp cho bạn ngăn xếp tức thời và mục nhập dưới cùng trong ngăn xếp sẽ là tệp và dòng nơi toán tử thực sự được gọi. –

0

bạn có thể tạo ra một số lượng nhỏ loại chủ sở hữu có tất cả các thuộc tính bạn muốn, sau đó sử dụng loại đó để giao tiếp với các loại khác, chẳng hạn như Money.

7

tôi có thể nghĩ đến hai cách để cung cấp này:

  • hoặc sử dụng mẫu và "khái niệm" kiểm tra
  • hoặc sử dụng "cấm" quá tải

Sử dụng quá tải là Giải pháp C++ 11. C++ 11 giới thiệu từ khóa delete đặc biệt cho trường hợp của bạn!

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; 

Cách cũ (C++ 03) để đi về đó là đôi:

  • trong lớp, kê khai các phương pháp private
  • không xác định phương pháp và chờ cho người liên kết để khiếu nại

Cách thứ hai là biện pháp bảo vệ trong trường hợp phương thức của lớp và cách duy nhất trong trường hợp phương thức miễn phí. Thật buồn rằng nó chỉ được phát hiện tại liên kết thời gian ... và từ khóa delete chỉ là đẹp hơn nhiều;)


Sử dụng mẫu là một giải pháp khác. Bạn có thể sử dụng hoặc std::enable_if hoặc static_assert: một sẽ loại bỏ các chức năng từ tập quá tải (SFINAE) trong khi người kia sẽ làm cho instantiation thất bại (lỗi trình biên dịch).

Ví dụ:

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

Các ví dụ cho static_assert là tự nhiên hơn (nó giống như một khẳng định thường xuyên, thực sự).


Tôi muốn giới thiệu quá tải + delete nếu bạn có. Nếu bạn không thì một dự phòng cho trường hợp mẫu có lẽ là giải pháp tốt nhất, bởi vì nó dễ dàng hơn để sửa lỗi trình biên dịch so với các trình liên kết.

+0

Cảm ơn, về cơ bản bạn đã tóm tắt các câu trả lời khác, cộng thêm 'enable_if' trông thú vị (' delete' không hoạt động với MSVC2010, 'static_assert' tạo lỗi trình biên dịch trong Money, không phải nơi tôi sử dụng toán tử). Tuy nhiên, tôi không thể có được bạn code 'enable_if' ví dụ để làm việc. Mã có vẻ tốt, vì vậy nỗi sợ của tôi là 'enable_if' là một tính năng C++ 11 khác mà MSVC2010 không hỗ trợ ... – Robin

+0

@Robin: bạn đang sử dụng' enable_if' nào? FYI có sự khác biệt nhỏ về giao diện giữa 'std :: enable_if' và' boost :: enable_if', 'std :: enable_if' tương đương với' boost :: enable_if_c'. Giải pháp Boost hoạt động trong C++ 03 và C++ 11, và giải pháp std chỉ cướp bóc nó, vì không có chức năng mới yêu cầu nó nên hoạt động. –

Các vấn đề liên quan