다음 프로그램을 고려하십시오.
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
clyde
의 주소 는 어떻게 얻 습니까?
모든 유형의 객체에 똑같이 잘 작동하는 솔루션을 찾고 있습니다. C ++ 03 솔루션은 좋지만 C ++ 11 솔루션에도 관심이 있습니다. 가능하면 구현 별 동작을 피하십시오.
C ++ 11의 std::addressof
함수 템플릿을 알고 있지만 여기에서 사용하는 데 관심이 없습니다. 표준 라이브러리 구현 자가이 함수 템플릿을 구현하는 방법을 이해하고 싶습니다.
답변
업데이트 : C ++ 11에서는 std::addressof
대신 사용할 수 있습니다 boost::addressof
.
먼저 비트에서 컴파일러 작업을 빼고 Boost에서 코드를 복사 해 보겠습니다.
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 );
}
함수에 대한 참조를 전달하면 어떻게됩니까 ?
참고 : addressof
함수에 대한 포인터와 함께 사용할 수 없습니다
C ++에서 if void func();
가 선언되면 func
인수를 사용하지 않고 결과를 반환하지 않는 함수에 대한 참조입니다. 기능이 참조는 소소 함수에 대한 포인터로 변환 할 수있다 -에서 @Konstantin
: 13.3.3.2 양쪽에 따르면 T &
및 T *
기능과 구별된다. 첫 번째는 Identity 변환이고 두 번째는 “정확한 일치”순위 (13.3.3.1.1 표 9)를 갖는 함수-포인터 변환입니다.
함수 참조 통과 addr_impl_ref
의 선택에 대한 과부하 해상도 모호성가, f
더미 인수에 확실히 해결, 0
이며, int
제 1 및 승격 될 수있다 long
(적분 변환)이.
따라서 우리는 단순히 포인터를 반환합니다.
변환 연산자를 사용하여 형식을 전달하면 어떻게됩니까?
변환 연산자가 a T*
를 산출 하면 모호함이 있습니다. f(T&,long)
두 번째 인수에는 Integral Promotion이 필요하지만 f(T*,int)
변환 연산자는 첫 번째에 호출됩니다 (@litb 덕분)
때이다 addr_impl_ref
. C ++ 표준의 의무의 차기 변환 순서가 가장 한 사용자 정의 변환에 포함 할 수있다. 형식을 래핑하고 addr_impl_ref
변환 순서를 이미 사용함으로써 형식과 함께 제공되는 변환 연산자를 “비활성화”합니다.
따라서 f(T&,long)
과부하가 선택됩니다 (및 Integral Promotion이 수행됨).
다른 유형은 어떻게됩니까?
따라서 f(T&,long)
유형이 T*
매개 변수 와 일치하지 않기 때문에 과부하가 선택 됩니다.
참고 : Borland 호환성과 관련하여 파일의 설명에서 배열은 포인터로 붕괴되지 않지만 참조로 전달됩니다.
이 과부하는 어떻게됩니까?
operator&
오버로드되었을 수 있으므로 유형에 적용하지 않기를 원합니다 .
reinterpret_cast
이 작업에 사용될 수있는 표준이 보장 됩니다 (@Matteo Italia의 답변 : 5.2.10 / 10 참조).
Boost는 컴파일러 경고를 피하기 위해 const
및 volatile
한정자를 사용하여 멋진 기능을 추가합니다 (및 적절하게 사용 const_cast
하여 제거).
- 캐스트
T&
로char const volatile&
- 스트립
const
및volatile
&
주소를 취하기 위해 연산자를 적용- 다시 캐스트
T*
const
/ volatile
저글링은 마술의 약간이지만 (오히려 4 과부하를 제공하는 대신) 작업을 단순화한다. 이후주의 T
규정되지 않은, 우리가 통과하는 경우는 ghost const&
, 다음 T*
입니다 ghost const*
따라서 예선 정말 손실되지 않았습니다.
편집 : 포인터 오버로드는 함수에 대한 포인터에 사용됩니다. 위의 설명을 약간 수정했습니다. 그래도 왜 필요한지 이해하지 못합니다 .
다음의 이데 오네 출력은 이것을 요약합니다.
답변
사용하십시오 std::addressof
.
배후에서 다음을 수행하는 것으로 생각할 수 있습니다.
- 객체를 참조에 대한 문자로 재 해석
- 그 주소를 취하십시오 (오버로드를 호출하지 마십시오)
- 포인터를 타입의 포인터로 다시 캐스트하십시오.
기존 구현 (Boost.Addressof 포함)은 추가 관리 const
와 volatile
자격 을 갖추기 만하면 됩니다.
답변
트릭의 뒤에 boost::addressof
@Luc 당통 제공하고 구현의 마법에 의존 reinterpret_cast
; 표준은 §5.2.10 ¶10에서 명시 적으로
“pointer to ” 유형의 표현식을 a를 사용하여 “pointer to ” 유형으로 명시 적으로 변환 할 수있는 경우 유형의 lvalue 표현식을
T1
“reference toT2
” 유형으로 캐스트 할T1
수 있습니다 . 즉, 참조 캐스트 는 내장 및 연산자를 사용한 변환 과 동일한 효과를 갖습니다 . 결과는 소스 lvalue와 동일한 객체를 참조하지만 다른 유형을 갖는 lvalue입니다.T2
reinterpret_cast
reinterpret_cast<T&>(x)
*reinterpret_cast<T*>(&x)
&
*
이제 임의의 객체 참조를 char &
(참조가 cv-qualified 인 경우 cv 규정을 사용하여)로 변환 할 수 있습니다. 포인터는 (cv-qualified)로 변환 할 수 있기 때문 char *
입니다. 이제 우리는 char &
객체를 오버로드하는 연산자가 더 이상 관련이 없으며 내장 &
연산자로 주소를 얻을 수 있습니다 .
부스트 구현은 cv-qualified 객체로 작업하기위한 몇 가지 단계를 추가합니다. 첫 번째 reinterpret_cast
작업은에 수행됩니다 const volatile char &
. 그렇지 않으면 일반 char &
캐스트가 작동하지 const
않거나 volatile
참조에 대해 작동 reinterpret_cast
하지 않습니다 (제거 할 수 없음 const
). 그런 다음 const
및 volatile
로 제거되고 const_cast
주소는 로 변경되고 “올바른”유형 &
의 최종 reinterpet_cast
이 완료됩니다.
가 const_cast
을 제거 할 필요가 const
/ volatile
이는 const가 아닌 / 휘발성 참조에 추가되었습니다 수 있지만, 그것은 무엇 없습니다 “해”수행 const
/의 volatile
최종 있기 때문에, 처음에 참조 reinterpret_cast
가 있다면 의지가 CV-자격을 다시 추가 처음에는 ( reinterpret_cast
제거 const
할 수 는 없지만 추가 할 수 있습니다).
의 나머지 코드는 addressof.hpp
대부분이 해결 방법 인 것으로 보입니다. 는 static inline T * f( T * v, int )
단지 볼랜드 컴파일러에 필요한 것 같다, 그러나 그것의 존재에 대한 필요성 소개하고 addr_impl_ref
, 그렇지 않으면 포인터 타입이 두 번째 오버로드에 의해 잡힐 것을.
편집 : 다양한 과부하에는 다른 기능이 있습니다. @Matthieu M. 우수한 답변을 참조하십시오.
글쎄, 나는 더 이상 이것을 확신하지 못한다. 그 코드를 더 조사해야하지만 이제 저녁 식사를 요리하고 있습니다 :), 나중에 살펴 보겠습니다.
답변
나는 이것을하는 구현을 보았다 addressof
.
char* start = &reinterpret_cast<char&>(clyde);
ghost* pointer_to_clyde = reinterpret_cast<ghost*>(start);
이것이 어떻게 적합한 지 묻지 마십시오!