2012-03-22 34 views
7

Tôi đặt câu hỏi này theo vấn đề tôi nêu ra here.Toán tử chuyển đổi được triển khai với static_cast

Điểm này khá đơn giản. Giả sử bạn có hai lớp học của loại này:

template < class Derived > 
class Base { 
... 
operator const Derived&() const { 
    return static_cast< const Derived& >(*this); 
    } 
... 
}; 

class Specialization : public Base<Specialization> { 
... 
}; 

Sau đó, giả sử bạn có một loại chuyển đổi như thế này:

template < class T > 
functionCall(const Base<T>& param) { 
    const T & val(param); 
    ... 
} 

Câu hỏi đặt ra là: gì cần được các tiêu chuẩn phù hợp với hành vi của chuyển đổi này?

Nếu nó giống như const T & val(static_cast<const T &> (param)) hoặc nó sẽ đệ quy lặp lại cho đến khi tràn ngăn xếp? Lưu ý rằng tôi có được hành vi đầu tiên biên dịch với GNU g++ và biên dịch thứ hai với Intel icpc.

Tôi đã cố gắng xem xét tiêu chuẩn (phần 5.9 trên static_cast và phần 12.3 về chuyển đổi) nhưng do thiếu kinh nghiệm nên tôi không thể tìm ra câu trả lời.

Tôi cảm ơn trước rất nhiều vì đã dành thời gian giúp tôi với điều này.

+2

Phần có liên quan sẽ là phần có độ phân giải quá tải. –

+0

Nếu bạn hài lòng với sự tò mò của tôi - tại sao bạn muốn toán tử chuyển đổi trong 'Base'? –

+1

@ Tony Delroy: 1) để tiết kiệm hơn một cách rõ ràng gọi phương thức lớp.2) Tôi đã tìm thấy loại mã này một vài lần trên web trong khi cố gắng tìm hiểu về các CRTP, xem ví dụ [tại đây] (http : //en.wikipedia.org/wiki/Expression_templates) 3) Sau khi tìm thấy hành vi kỳ lạ này tôi đã thực sự tò mò mà nên là một trong những chính xác :-) – Massimiliano

Trả lời

3

Nhìn vào [expr.static.cast] trong n3337 (dự thảo đầu tiên làm việc, kể Standard):

2/ Một vế trái của loại “CV1 B”, nơi B là một loại lớp, có thể được đúc để nhập “tham chiếu đến cv2 D”, trong đó D là một lớp bắt nguồn (khoản 10) từ B, nếu chuyển đổi chuẩn hợp lệ từ “con trỏ thành D” thành “pointer to B” tồn tại [.. .]

4/ Nếu không, một biểu e thể được chuyển đổi một cách rõ ràng cho một loại T sử dụng một static_cast dạng static_cast<T>(e) nếu việc kê khai T t(e); là tốt được hình thành, đối với một số phát minh tạm thời biến t [..]

Vì vậy, tôi sẽ giải thích hành vi của gcc đó là điều đúng, tức là sự biểu hiện:

static_cast<Derived const&>(*this) 

không nên gọi đệ quy operator Derived const&() const.

Tôi suy ra điều này từ sự hiện diện của Nếu không, từ khóa ngụ ý sắp xếp các quy tắc. Quy tắc 2/ phải được thử trước quy tắc 4/.

0

Không nên sử dụng toán tử chuyển đổi ngầm. Trong C++ 11, bạn có thể thêm từ khóa explicit không chỉ vào các hàm tạo đối số đơn lẻ mà còn cho các toán tử chuyển đổi. Đối với mã C++ 03, bạn có thể sử dụng hàm chuyển đổi được đặt tên rõ ràng như self() hoặc down_cast().

Hơn nữa, bạn dường như đang sử dụng lớp Base cho CRTP, tức là để bật tính đa hình tĩnh. Điều đó có nghĩa là bạn phải biết tại thời gian biên dịch mà cụ thể là Derived lớp bạn đang gọi. Do đó, bạn không cần phải sử dụng tài liệu tham khảo const Base& trong bất kỳ mã công cộng nào, ngoại trừ việc triển khai giao diện CRTP.

Trong các dự án của tôi, tôi có một lớp mẫu enable_crtp:

#include <type_traits> 
#include <boost/static_assert.hpp> 

template 
< 
     typename Derived 
> 
class enable_crtp 
{ 
public: 
     const Derived& self() const 
     { 
       return down_cast(*this); 
     } 

     Derived& self() 
     { 
       return down_cast(*this); 
     } 

protected: 
     // disable deletion of Derived* through Base* 
     // enable deletion of Base* through Derived* 
     ~enable_crtp() 
     { 
       // no-op 
     } 

private: 
     // typedefs 
     typedef enable_crtp Base; 

     // cast a Base& to a Derived& (i.e. "down" the class hierarchy) 
     const Derived& down_cast(const Base& other) const 
     { 
       BOOST_STATIC_ASSERT((std::is_base_of<Base, Derived>::value)); 
       return static_cast<const Derived&>(other); 
     } 

     // cast a Base& to a Derived& (i.e. "down" the class hierarchy) 
     Derived& down_cast(Base& other) 
     { 
     // write the non-const version in terms of the const version 
     // Effective C++ 3rd ed., Item 3 (p. 24-25) 
     return const_cast<Derived&>(down_cast(static_cast<const Base&>(other))); 
     } 
}; 

Lớp này được tư nhân có nguồn gốc từ bất kỳ ISomeClass lớp cơ sở CRTP như thế này:

template<typename Impl> 
class ISomeClass 
: 
    private enable_crtp<Impl> 
{ 
public: 
    // interface to be implemented by derived class Impl 
    void fun1() const 
    { 
     self().do_fun1(); 
    } 

    void fun2() 
    { 
     self().do_fun2() 
    } 

protected: 
    ~ISomeClass() 
    {} 
}; 

Các lớp có nguồn gốc khác nhau có thể thực hiện giao diện này theo cách riêng của họ như sau:

class SomeImpl 
: 
    public ISomeClass<SomeImpl> 
{ 
public: 
    // structors etc. 

private: 
    // implementation of interface ISomeClass 

    friend class ISomeClass<SomeImpl>; 

    void do_fun1() const 
    { 
     // whatever 
    } 

    void do_fun2() 
    { 
     // whatever 
    } 

    // data representation 
    // ... 
}; 

Gọi mã bên ngoài fun1 của class SomeImpl sẽ được chuyển đến phiên bản const hoặc không const thích hợp của self() trong class enable_crtp và sau khi down_cast việc triển khai do_fun1 sẽ được gọi. Với một trình biên dịch tốt, tất cả các indirections nên được tối ưu hóa hoàn toàn.

LƯU Ý: các destructors được bảo vệ của ISomeClassenable_crtp làm cho mã an toàn đối với người dùng cố gắng xóa SomeImpl* đối tượng thông qua con trỏ cơ sở.

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