Cập nhật: trong C ++ 11, người ta có thể sử dụng std::addressof
thay vì boost::addressof
.
Trước tiên chúng ta hãy sao chép mã từ Boost, trừ đi trình biên dịch hoạt động xung quanh các bit:
template<class T>
struct addr_impl_ref
{
T & v_;
inline addr_impl_ref( T & v ): v_( v ) {}
inline operator T& () const { return v_; }
private:
addr_impl_ref & operator=(const addr_impl_ref &);
};
template<class T>
struct addressof_impl
{
static inline T * f( T & v, long ) {
return reinterpret_cast<T*>(
&const_cast<char&>(reinterpret_cast<const volatile char &>(v)));
}
static inline T * f( T * v, int ) { return v; }
};
template<class T>
T * addressof( T & v ) {
return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 );
}
Điều gì xảy ra nếu chúng ta vượt qua một tham chiếu đến chức năng ?
Lưu ý: addressof
không thể được sử dụng với một con trỏ để hoạt động
Trong C ++ nếu void func();
được khai báo, thì func
tham chiếu đến một hàm không có đối số và không trả về kết quả. Tham chiếu này đến một chức năng có thể được chuyển đổi một cách tầm thường thành một con trỏ thành chức năng - từ @Konstantin
: Theo 13.3.3.2 cả hai T &
và T *
không thể phân biệt cho các chức năng. Cái đầu tiên là một chuyển đổi Nhận dạng và cái thứ hai là chuyển đổi Hàm-Con trỏ cả hai đều có thứ hạng "Kết hợp chính xác" (13.3.3.1.1 bảng 9).
Các tham chiếu đến chức năng đi qua addr_impl_ref
, có một sự nhập nhằng trong việc giải quyết tình trạng quá tải cho các lựa chọn f
, được giải quyết nhờ vào lập luận giả 0
, mà là một int
đầu tiên và có thể được đề bạt lên một long
(chuyển đổi Integral).
Vì vậy, chúng tôi chỉ đơn giản trả về con trỏ.
Điều gì xảy ra nếu chúng ta vượt qua một loại với toán tử chuyển đổi?
Nếu toán tử chuyển đổi mang lại một giá trị T*
thì chúng ta có một sự mơ hồ: đối với f(T&,long)
Khuyến mãi tích hợp là bắt buộc đối với đối số thứ hai trong khi đối f(T*,int)
với toán tử chuyển đổi được gọi vào lần đầu tiên (nhờ @litb)
Đó là khi addr_impl_ref
bắt đầu. Tiêu chuẩn C ++ bắt buộc một chuỗi chuyển đổi có thể chứa tối đa một chuyển đổi do người dùng xác định. Bằng cách gói loại addr_impl_ref
và buộc sử dụng trình tự chuyển đổi, chúng tôi "vô hiệu hóa" bất kỳ toán tử chuyển đổi nào mà loại đi kèm.
Do đó, f(T&,long)
quá tải được chọn (và Khuyến mãi tích hợp được thực hiện).
Điều gì xảy ra cho bất kỳ loại khác?
Do đó, f(T&,long)
quá tải được chọn, vì có loại không khớp với T*
tham số.
Lưu ý: từ các nhận xét trong tệp liên quan đến khả năng tương thích Borland, các mảng không phân rã thành các con trỏ, nhưng được chuyển qua tham chiếu.
Điều gì xảy ra trong tình trạng quá tải này?
Chúng tôi muốn tránh áp dụng operator&
cho các loại, vì nó có thể đã bị quá tải.
Tiêu chuẩn đảm bảo reinterpret_cast
có thể được sử dụng cho công việc này (xem câu trả lời của @Matteo Italia: 5.2.10 / 10).
Boost thêm một số niceties với const
và volatile
vòng loại để tránh cảnh báo trình biên dịch (và sử dụng đúng cách const_cast
để loại bỏ chúng).
- Truyền
T&
tớichar const volatile&
- Tách
const
vàvolatile
- Áp dụng
&
toán tử để lấy địa chỉ
- Quay trở lại một
T*
Các const
/ volatile
tung hứng là một chút ma thuật đen, nhưng nó đơn giản hóa công việc (hơn là cung cấp 4 quá tải). Lưu ý rằng kể từ khi T
là không đủ tiêu chuẩn, nếu chúng ta vượt qua một ghost const&
, sau đó T*
là ghost const*
, do đó vòng loại đã không thực sự bị mất.
EDIT: quá tải con trỏ được sử dụng cho con trỏ đến các chức năng, tôi đã sửa đổi phần giải thích ở trên. Tôi vẫn không hiểu tại sao nó lại cần thiết .
Đầu ra ideone sau đây tổng hợp điều này, phần nào.