2011-12-13 32 views
5

Trong C++, tôi muốn có một lớp học mà nhà xây dựng như sau:Làm thế nào để kiểm tra xem một loại là một typedef int

class A { 
    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 
}; 

Vấn đề ở đây là nếu người dùng khởi tạo với

A a(0); 

Sau đó, trên hệ thống 64 bit, trình biên dịch sẽ phàn nàn rằng không biết liệu 0 có nên được chuyển thành A* hoặc intptr_t, đủ công bằng không. Vì tôi muốn ký hiệu đơn giản này hoạt động, tôi đã thêm hàm tạo sau:

explicit A(int a) { assert(a==0); ... } 

Xác nhận là vì đây là số nguyên duy nhất có ý nghĩa. Bây giờ, vấn đề phát sinh với một hệ thống 32 bit, trong đó intptr_t thực sự là ... int! Vì vậy, bây giờ, hệ thống than phiền có hai nhà xây dựng lấy cùng một kiểu tham số (trong đó, một lần nữa, là công bằng đủ). Vì vậy, câu hỏi của tôi là: có cách nào với bộ tiền xử lý phát hiện rằng intptr_t thực sự là int và, trong trường hợp đó, không biên dịch hàm tạo với int. Hoặc, có một cách khác để làm cho ký hiệu A a(0) hợp lệ mà không thêm hàm tạo với int, nhưng không loại bỏ một trong hai hàm tạo đầu tiên (và không làm cho chúng ngầm định).

+0

Bạn không thể loại bỏ hàm tạo A (A * khác) và thay thế bằng A (A & khác). Nó sẽ có vẻ tự nhiên hơn với tôi (giả sử bạn đang sao chép khác và không chaining). –

Trả lời

3

Something như

#if INTPTR_MAX == INT_MAX 

thể làm như lừa, nhưng nó vẫn sẽ cho kết quả đúng nơi longint có cùng kích thước, và ptrint_t là một typedef cho long. Một khả năng khác (nhưng không biết liệu bạn có thể sử dụng nó hay không) sẽ sử dụng uintptr_t, thay vì intptr_t.

Ngoài những điều này: bộ xử lý trước không biết về các loại, do đó, vấn đề không thể giải quyết được ở đó. Bạn sẽ phải sử dụng một số loại chương trình meta meta-programming: bạn làm cho một constructor int một mẫu, sử dụng boost::enable_if để kích hoạt nó chỉ khi đối số có loại int. Nếu ptrint_tint, thì chức năng được kích hoạt sẽ không bao giờ được sử dụng vì nó sẽ không bao giờ phù hợp hơn chức năng không có mẫu có cùng chữ ký. Nếu ptrint_t không phải là int, thì việc khởi tạo mẫu sẽ tốt hơn khi đối số có loại int. (Lưu ý rằng tôi chưa bao giờ thử bản thân mình: nghe có vẻ như tôi thích nó nên có thể, nhưng tôi không quen thuộc với boost::enable_if đến hãy chắc chắn.)

+0

Cảm ơn, cuối cùng, sử dụng 'uintptr_t' được giải quyết bằng vấn đề! – PierreBdR

+0

@PierreBdR Các giải pháp đơn giản nhất là :-)! –

1

Tại sao bạn không thực hiện đơn giản một tham số hàm tạo hoạt động như thể other0?Nếu vì lý do nào đó mà bạn không muốn, tôi khuyên bạn nên sử dụng các đặc điểm kiểu, miễn là bạn có quyền truy cập vào trình biên dịch C++ 11 hoặc tăng lên:

Bạn có thể loại bỏ xác nhận tĩnh và kiểm tra kiểu, nhưng sau đó thay vì lỗi biên dịch bạn sẽ nhận được một cảnh báo (tùy thuộc vào mức độ cảnh báo của bạn, thậm chí có thể được biến thành một lỗi) khi bạn làm như sau:

A a(0.0f); 
+0

Nó có thể, nhưng có hai nhược điểm: nó phức tạp, bất kỳ việc sử dụng các nhà xây dựng với một loại sai sẽ cung cấp cho một lỗi không rõ ràng. – PierreBdR

+0

@PierreBdR Chính xác thì bạn thấy gì phức tạp ở đây? Xác nhận tĩnh là tự giải thích. Tương tự đối với các thông báo lỗi do chúng tạo ra. "Đối số phải là tích phân" thực sự không rõ ràng? – gwiazdorrr

+0

Đó là một vấn đề của việc sử dụng một chức năng mẫu để giải quyết một vấn đề không được liên kết với các mẫu ở nơi đầu tiên. Dù sao, như bạn có thể thấy, giải pháp ưa thích của tôi đã thay đổi từ 'intptr_t' thành' uintptr_t', nghĩa là bạn sẽ thừa nhận, đơn giản hơn. – PierreBdR

-1

là có một cách khác để làm cho các ký hiệu A a(0) hợp lệ

Chỉ cần giới thiệu một hàm tạo template.

class A { 
public: 
    template<typename T> 
    explicit A (T t) { assert(t==0); } // explicit matters ? 

    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 
}; 

Điều này sẽ giải quyết vấn đề 32 bit và 64 bit của bạn!

+1

Và cách giải quyết vấn đề này? Lỗi truy cập thay vì lỗi mơ hồ? – gwiazdorrr

+0

@gwiazdorrr, tôi đã sửa đổi bài đăng. Xem câu trả lời được cập nhật. Tôi nghĩ OP muốn hạn chế '0'. Tuy nhiên, trong câu hỏi nó được đề cập đến như là 'assert()', có nghĩa là cách khác. Tôi sửa nó rồi. – iammilind

+0

Tại sao một downvote? – iammilind

-1

Bạn cũng có thể vượt qua một tham số, mà quyết định cái nào được gọi là:

struct TakePtr{}; 
struct TakeInt{}; 

class A { 
    A(A* other, const TakePtr&) { ... } 
    A(intptr_t other, const TakeInt&) { ... } 
}; 

Bằng cách này bạn có thể chắc chắn đó constructor được gọi là:

A a2(0, TakeInt());  // calls the 2nd constructor, taking int 
A a1(&a2, TakePtr()); // calls the 1st constructor, taking a pointer 
+1

Đây không phải là cách hay để hoàn thành nhiệm vụ. Nó giới thiệu không cần thiết [khả năng đọc mã mùi] (http://en.wikipedia.org/wiki/Code_smell#Common_code_smells). Nó có thể được thực hiện theo cách đơn giản hơn. – iammilind

+0

@iammilind Vì vậy, một trong những mục chính xác từ danh sách đó là các mã trên phá vỡ? Hoặc là nhiều mặt hàng? Đoạn mã trên sẽ ngăn chặn bất kỳ chuyển đổi nào và đang sử dụng hàm tạo cụ thể để tạo một đối tượng. –

+0

'Hoặc có nhiều mục hơn'; vâng, điều đó là không cần thiết. Mã của bạn sẽ hoàn thành nhiệm vụ, nhưng đồng thời, trình coder cần phải được xử lý kỷ luật về việc chuyển loại thích hợp; do đó mất trừu tượng của nó. Ngoài ra, loại lỗi này có thể không được chú ý âm thầm: 'A a2 (0, TakePtr()); // oops nó phải là TakeInt() ' – iammilind

0

Tôi cho rằng điều đơn giản nhất là khai báo tất cả 6 hàm tạo (int, long, long long, và các biến thể unsigned của chúng), thay vì sử dụng intptr_t.

+0

Vậy tại sao không có giải pháp 'template' đơn giản hơn? – iammilind

+2

Tôi nghĩ rằng đây là quá mức cần thiết ... – PierreBdR

+0

Đây là giải pháp đơn giản và hiệu quả cho vấn đề bạn đã nêu –

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