operator &가 오버로드 될 때 어떻게 안정적으로 객체의 주소를 얻을 수 있습니까? //

다음 프로그램을 고려하십시오.

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는 컴파일러 경고를 피하기 위해 constvolatile한정자를 사용하여 멋진 기능을 추가합니다 (및 적절하게 사용 const_cast하여 제거).

  • 캐스트 T&char const volatile&
  • 스트립 constvolatile
  • &주소를 취하기 위해 연산자를 적용
  • 다시 캐스트 T*

const/ volatile저글링은 마술의 약간이지만 (오히려 4 과부하를 제공하는 대신) 작업을 단순화한다. 이후주의 T규정되지 않은, 우리가 통과하는 경우는 ghost const&, 다음 T*입니다 ghost const*따라서 예선 정말 손실되지 않았습니다.

편집 : 포인터 오버로드는 함수에 대한 포인터에 사용됩니다. 위의 설명을 약간 수정했습니다. 그래도 왜 필요한지 이해하지 못합니다 .

다음의 이데 오네 출력은 이것을 요약합니다.


답변

사용하십시오 std::addressof.

배후에서 다음을 수행하는 것으로 생각할 수 있습니다.

  1. 객체를 참조에 대한 문자로 재 해석
  2. 그 주소를 취하십시오 (오버로드를 호출하지 마십시오)
  3. 포인터를 타입의 포인터로 다시 캐스트하십시오.

기존 구현 (Boost.Addressof 포함)은 추가 관리 constvolatile자격 을 갖추기 만하면 됩니다.


답변

트릭의 뒤에 boost::addressof@Luc 당통 제공하고 구현의 마법에 의존 reinterpret_cast; 표준은 §5.2.10 ¶10에서 명시 적으로

“pointer to ” 유형의 표현식을 a를 사용하여 “pointer to ” 유형으로 명시 적으로 변환 할 수있는 경우 유형의 lvalue 표현식을 T1“reference to T2” 유형으로 캐스트 할 T1수 있습니다 . 즉, 참조 캐스트 는 내장 및 연산자를 사용한 변환 과 동일한 효과를 갖습니다 . 결과는 소스 lvalue와 동일한 객체를 참조하지만 다른 유형을 갖는 lvalue입니다.T2reinterpret_castreinterpret_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). 그런 다음 constvolatile로 제거되고 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);

이것이 어떻게 적합한 지 묻지 마십시오!


답변

boost :: addressof 와 그 구현을 살펴보십시오 .


답변