2010-07-17 29 views
17

auto_ptr (shared_ptr) cố gắng sử dụng chúng càng rõ ràng càng tốt; đó là, lý tưởng, bạn không nên có thể nói một sự khác biệt cho dù bạn đang sử dụng một auto_ptr hoặc một con trỏ thực sự cho một đối tượng. Xem xét:Tại sao auto_ptr không hỗ trợ op -> *()

class MyClass 
{ 
public: 
    void foo() { } 
}; 

MyClass* p = new MyClass; 
auto_ptr<MyClass> ap(new MyClassp); 

p->foo();  // No notational difference in using real 
ap->foo();  // pointers and auto_ptrs 

Khi bạn cố gắng để gọi một hàm thành viên thông qua một thành viên con trỏ-to-, có một sự khác biệt, như auto_ptr rõ ràng là không thực hiện op -> *():

void (MyClass::*memfun)() = &MyClass::foo; 

(p->*memfun)();   // OK 
(ap->*memfun)();  // Error op->*() missing 
(ap.get()->*memfun)(); // OK 

Tại sao không có hỗ trợ cho op -> *() trong auto_ptr và làm thế nào sẽ thực hiện nó (Tôi đã thử nghiệm một thời gian, nhưng cuối cùng đã bỏ).

+2

Đây là một câu hỏi rất hay; không có con trỏ thông minh nào hỗ trợ '-> *'. Trong ví dụ của bạn, '((* ap). * Memfun)()' cũng hợp lệ. –

+1

Tại sao không chỉ sử dụng .get()? (ap.get() -> * memfun)(); – Puppy

Trả lời

4

thực hiện -> * sẽ yêu cầu để giải quyết vấn đề chuyển tiếp hoàn hảo:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm

điều hành -> * sẽ phải trả về một đối tượng có thể được gọi với cùng danh sách tham số là đối tượng con trỏ-to-thành viên , xử lý đúng các loại tham chiếu, độ biến động và tham chiếu. Và sau đó nó sẽ phải sử dụng sức mạnh ma thuật đặc biệt để xử lý các thông số mặc định. Điều này là khó khăn, dễ bị lỗi, không thể giải quyết được và ăn quá nhiều thời gian biên dịch và vì con trỏ tới thành viên là một tính năng khá phổ biến của C++, chúng thường bị loại bỏ khỏi việc triển khai con trỏ thông minh.

+1

Bạn không thể chỉ định các tham số mặc định cho các con trỏ hàm, vì vậy bạn có thể tấn công điều đó và trong khi khó khăn thì nó không thể giải quyết được. –

+1

Lưu ý rằng [# 3] (http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm#s3) trong bài báo không áp dụng ở đây. Thông qua việc khấu trừ chúng ta biết các kiểu đối số của hàm thành viên là gì và có thể sử dụng một hàm meta để ánh xạ nó tới kiểu được yêu cầu, do đó làm giảm số lượng các quá tải cần thiết thành 'N'. –

+0

Bạn nói đúng, tôi không nhận thấy điều này. –

0

Tôi có thể sai, nhưng tôi nghĩ không có cách nào để quá tải operator->* cho chức năng trỏ đến thành viên. Điều này là do p->*memfun, trong khi hợp lệ như là một phần của một biểu thức coi đó là một đối tượng có thể gọi, không phải là biểu thức hợp lệ theo đúng nghĩa của nó và không có loại. Do đó, không có kiểu hợp lệ để toán tử trả về.

Sau đây sẽ làm việc cho một con trỏ đến thành viên, nhưng cố gắng sử dụng nó cho hàm con trỏ tới thành viên cho lỗi, "sử dụng không hợp lệ chức năng thành viên không tĩnh", với GCC và lỗi trình biên dịch nội bộ với MSVC.

template <class CLASS, typename MEMBER> 
MEMBER& operator->*(std::auto_ptr<CLASS>& p, MEMBER CLASS::*m) 
{ 
    return (*p).*m; 
} 

EDIT: là câu trả lời Georg của chỉ ra, bạn có thể sử dụng boost::bind hoặc tương tự để tạo ra một tập hợp các quá tải cho các chức năng thành viên lên đến một số lượng tối đa cố định của đối số, nhưng vẫn còn có cách nào để nạp chồng toán tử cho tất cả chức năng thành viên có thể.

+0

Đó phải là '(* p). * M'. Ngoài ra, toán tử nhận các con trỏ hàm thành viên làm đối số thứ hai của nó - do đó loại của chúng có sẵn và có thể được sử dụng để xác định kiểu trả về. –

+0

@Georg: cảm ơn, đó là lỗi cắt và dán, đã được khắc phục ngay bây giờ. Nó vẫn không biên dịch, vì không có kiểu nào khớp với 'MEMBER' nếu' m' là một con trỏ hàm thành viên. –

+0

Bạn phải quá tải với toán tử '... -> * (..., R (Class :: * mfp) (ArgTypes ...))' cho điều đó, xem câu trả lời của tôi. –

8

Vì Luther chỉ ra không tầm thường để thực hiện - nhưng điều đó là có thể.

Bạn phải

  1. mẫu sử dụng để loại các đối số operator->* có thể được suy luận
  2. chăm sóc vòng loại tốt và nhiều arities chức năng sử dụng quá tải
  3. cho con trỏ hàm thành viên trả về một callabe đối tượng đó là:
    • được liên kết với cá thể con trỏ thông minh trỏ tới
    • thực hiện một operator() với một chữ ký tương đương với chức năng thành viên

Bỏ qua vòng loại cho momement, đây là cách nó về cơ bản có thể nhìn (sử dụng C++ 0x để tránh tái diễn của nhãn hiệu):

// pointer to data member: 

template<class T, class D> 
D& operator->*(std::auto_ptr<T>& p, D T::*mp) { 
    return (*p).*mp; 
} 

// pointer to member function: 

template<class T, class R, class... Args> struct Callable { 
    typedef R (T::*MFP)(Args...); 
    MFP mfp; 
    T& instance; 

    Callable(T t, MFP mfp) : instance(t), mfp(mfp) {} 

    R operator()(Args... a) { 
     return (instance.*mfp)(a...); 
    } 
}; 

template<class T, class R, class... Args> 
Callable<T, R, Args...> 
operator->*(std::auto_ptr<T>& p, R (T::*mfp)(Args...)) { 
    return Callable<T, R, Args...>(*p, mfp); 
} 

Nhưng cuối cùng, tại sao phải bận tâm khi chúng ta chỉ có thể sử dụng các functors liên kết các con trỏ thành viên ngay từ đầu.

Trong khi tôi không thể chắc chắn về điều đó, nếu bạn kết hợp sự hiểu biết rằng

  • việc thực hiện là không tầm thường
  • có một sự thay thế dễ dàng mà làm việc chỉ là tốt ((*p).*m)

... có thể thường không được triển khai do tỷ lệ xấu của công việc cần thiết cho mức tăng thu được từ tính năng này.

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