2012-10-10 2 views
14

멤버 선택자가있는 template에서 offsetof을 사용해야 할 필요가 있습니다.C++ 템플릿 내부의 컴파일 타임 오프셋

template <typename T, 
      typename R, 
      R T::*M 
     > 
constexpr std::size_t offset_of() 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
}; 

사용법 (에서 가장 짜증나는) 완벽하지 않다 :

struct S 
{ 
    int x; 
    int y; 
}; 

static_assert(offset_of<S, int, &S::x>() == 0, ""); 
static_assert(offset_of<S, int, &S::y>() == sizeof(int), ""); 

constexpr 형태가 쉽게 나는 당신이 어색한 구문을 용서 것이다 경우, 수있는 방법을 마련했습니다 사용 :

template <typename T, typename R> 
std::size_t offset_of(R T::*M) 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
}; 

를이 컴파일시에 실시되지 않은 (그러나 쉽게 사용)하는 명백한 불이익 :

int main() 
{ 
    std::cout << offset_of(&S::x) << std::endl; 
    std::cout << offset_of(&S::y) << std::endl; 
} 

내가 찾고있는 문법은 이 아닌 constexpr과 유사하지만 여전히 완전히 컴파일 타임입니다. 그러나, 나는 그것에 대한 구문을 생각해 낼 수 없다. 또한 offset_of<&S::x>::value (나머지 유형 특성과 비슷 함)에 만족하지만 구문 마술을 이해할 수는 없습니다.

+0

나는 표준에서 그것이 이것이 당신이 기대하는 바가 무엇인지 말하는 것이라고 생각하고있다. 그러나 나는 그것을 발견 할 수 없다. –

+4

표준 ['offsetof']에 무슨 문제가 있습니까? (http://en.cppreference.com/w/cpp/types/offsetof)? –

+0

@NicolBolas 그렇지 않다고 생각합니다. 'nullptr'을 역 참조하지 말아야합니까? (그리고'->'는 역 참조로 간주됩니다) UB가 이미 있습니까? 그러나 다시, VC의 'offsetof'매크로 버전은 전혀 다르지 않습니다. 따라서 실제로는 정의되지 않은 것보다 구현이 정의 된 것일 수 있습니다. –

답변

13

다음은 (학점이 아이디어를 the answer to this question로 이동) 작동합니다 : GCC에서, 컴파일시에 사용할 수있는 내장 확장에 offsetof 매크로가 확장 (아래 참조) 것을

#include <cstddef> 

template <typename T, typename M> M get_member_type(M T::*); 
template <typename T, typename M> T get_class_type(M T::*); 

template <typename T, 
      typename R, 
      R T::*M 
     > 
constexpr std::size_t offset_of() 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
} 

#define OFFSET_OF(m) offset_of<decltype(get_class_type(m)), \ 
        decltype(get_member_type(m)), m>() 

struct S 
{ 
    int x; 
    int y; 
}; 

static_assert(OFFSET_OF(&S::x) == 0, ""); 

참고. 또한 코드가 UB를 호출하고, null 포인터를 역 참조하므로 실제로 작동 할 수도 있더라도 보장은 없습니다. 현재 GCC 코드를합니다 (bug report here 참조)를 수용하지만

루크 달톤 지적한 바와
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) 

상수 식은 C++ 11 표준에 따른 reinterpret_cast을 포함 할 수 없다. 또한 defect report 1384을 찾았고 덜 엄격한 규칙을 만드는 것에 대해 에 대해 이야기 했으므로 향후 변경 될 수 있습니다.

+0

그러면 나에게 잘 어울립니다. – hvd

+4

상수 표현식은 'reinterpret_cast'를 포함 할 수 없습니다 (평가되지 않은 경우 제외). –

+1

@LucDanton : 정보 주셔서 감사합니다. 또한 이에 대한 제한을 완화하는 방법에 대한 [결함 보고서 1384] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1384)도 발견되었습니다. –

관련 문제