2012-01-15 28 views
13

C++ 03 cho phép bạn đủ điều kiện tham số chức năng dưới dạng tham chiếu const, volatile và/hoặc giá trị lvalue (&).C++ 11: Tóm tắt về tham số const, volatile, lvalue và tham chiếu rvalue của hàm thành viên đủ điều kiện?

C++ 11 thêm một mục khác: tham chiếu rvalue (&&).

Hơn nữa, C++ cho phép bạn quá tải các chức năng dựa trên vòng loại của các tham số của chúng, sao cho quá tải thích hợp nhất được chọn khi gọi hàm.

Một hàm thành viên có thể được coi là một hàm có tham số phụ, có kiểu tham chiếu đến một thể hiện của lớp mà nó là một thành viên. Có thể quá tải một hàm thành viên dựa trên các vòng loại của 'tham số bổ sung' này giống như bất kỳ tham số nào khác. Này được thể hiện bằng cách đặt vòng loại ở phần cuối của hàm chữ ký:

struct Foo 
{ 
    int& data();    // return a non-const reference if `this` is non-const 
    const int& data() const; // return a const reference if `this` is const 
}; 

Trong C++ 03, constvolatile vòng loại là có thể, và C++ 11 cũng cho phép &&& (& về mặt lý thuyết có thể có được cho phép trong C++ 03, nhưng nó không được).

Bất kỳ kết hợp nào của vòng loại đều có thể được sử dụng, ngoại trừ &&& loại trừ lẫn nhau, giúp 2^2 = 4 khả năng trong C++ 03 và 2^4-4 = 12 trong C++ 11 . Điều này có thể khá đau khi bạn muốn làm việc với các con trỏ hàm thành viên, bởi vì chúng không phải là một chút đa hình trong các vòng loại này: các vòng loại trên loại "this" của một con trỏ hàm thành viên được truyền như một đối số phải khớp chính xác với loại tham số đang được truyền. C++ cũng không có cơ sở rõ ràng để trừu tượng hóa các vòng loại. Trong C++ 03, điều này phần lớn là OK, bởi vì bạn sẽ phải viết một phiên bản const và một phiên bản không phải là const và không ai quan tâm đến volatile, nhưng trong trường hợp bệnh lý trong C++ 11 (mà không phải là không phổ biến như nó là bệnh lý), bạn có thể phải tự viết tối đa 12 lần quá tải. Mỗi hàm.

Tôi rất vui khi phát hiện ra rằng nếu bạn đang chuyển loại lớp kèm theo dưới dạng tham số mẫu và lấy kiểu con trỏ hàm thành viên từ đó, các vòng loại constvolatile được phép và được truyền như bạn mong đợi :

template<typename Object> 
struct Bar 
{ 
    typedef int (Object::*Sig)(int); 
}; 

Bar<Baz>;    // Sig will be `int (Baz::*)(int)` 
Bar<const Baz>;   // Sig will be `int (Baz::*)(int) const` 
Bar<volatile Baz>;  // Sig will be `int (Baz::*)(int) volatile` 
Bar<const volatile Baz>; // Sig will be `int (Baz::*)(int) const volatile` 

Điều này thật tuyệt vời hơn là phải viết ra tất cả các trường hợp theo cách thủ công.

Thật không may, nó dường như không hoạt động cho &&&.

GCC 4.7 nói:

error: forming pointer to reference type ‘Baz&&’

Nhưng đó không phải là quá ngạc nhiên, cho rằng GCC là 4,7 chưa có hỗ trợ cho vòng loại tài liệu tham khảo trên this.

Tôi cũng đã thử nó với Clang 3.0, mà không có sự ủng hộ như vậy:

error: member pointer refers into non-class type 'Baz &&'

Oh, tốt.

Tôi có chính xác trong việc kết luận rằng điều này là không thể, và không có cách nào để tóm tắt các vòng loại tham chiếu trên "this type" của các con trỏ hàm thành viên? Bất kỳ kỹ thuật nào khác để trừu tượng hóa các vòng loại (đặc biệt là trên this) ngoài trường hợp cụ thể khi bạn chuyển "this loại" làm thông số mẫu cũng sẽ được đánh giá cao.

(Điều đáng chú ý là nếu C++ không phân biệt giữa chức năng thành viên và chức năng bình thường, điều này tất cả sẽ không quan trọng: bạn sẽ sử dụng tham số mẫu làm kiểu tham số của hàm (con trỏ) và đối số mẫu sẽ được chuyển qua nguyên trạng, vòng loại nguyên vẹn, không cần suy nghĩ thêm.)

+1

Tần suất bạn cần hành vi khác nhau cho tất cả các kết hợp vòng loại có thể có? (Hoặc, cho rằng vấn đề, thậm chí hai hoặc ba kết hợp có thể?) –

+1

Nếu tôi đang cố gắng để viết một trừu tượng cho các đối tượng giống như chức năng (theo tinh thần của std :: chức năng nhưng không giống nhau) sau đó đầy đủ là một mục tiêu thiết kế . Tôi thực sự không thể ngoại suy hay ước tính (như thường lệ) nhưng tôi biết rằng tôi đã chạy vào nó trước đây. – glaebhoerl

Trả lời

4

Bạn có nghĩ về việc chỉ đơn giản là chuyên về mẫu của bạn không?

Bạn chỉ có thể thêm hai phiên bản:

template <typename Object> 
struct Bar<Object&> { 
    typedef int (Object::*Sig)(int)&; 
}; 

template <typename Object> 
struct Bar<Object&&> { 
    typedef int (Object::*Sig)(int)&&; 
}; 

Và sau đó trình biên dịch sẽ chọn chuyên ngành đúng (hoặc dự phòng cho trường hợp chung) một cách thích hợp.

Điều này tiết kiệm cho bạn từ điều const/volatile, nhưng có nghĩa là bạn cần viết mã 3 lần.

+0

Vâng, thực tế là 'const' và' volatile' được xử lý thông minh có nghĩa là tôi chỉ phải viết ba phiên bản thay vì mười hai, đó là tốt đẹp. – glaebhoerl

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