2012-10-10 40 views
14

Tôi có nhu cầu sử dụng offsetof từ một số template với bộ chọn thành viên. Tôi đã tìm ra một cách, nếu bạn sẽ tha cú pháp vụng về:C++ Biên dịch bù thời gian bên trong một mẫu

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

Cách sử dụng là không hoàn hảo (gây phiền nhiễu lúc tốt nhất):

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), ""); 

Dạng phi constexpr là dễ dàng hơn sử dụng:

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

tại những bất lợi rõ ràng rằng nó không được thực hiện tại thời gian biên dịch (nhưng dễ dàng hơn để sử dụng):

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

Điều tôi đang tìm kiếm là cú pháp giống như số không phải là constexpr nhưng vẫn hoàn toàn biên dịch; tuy nhiên, tôi không thể đưa ra cú pháp cho nó. Tôi cũng sẽ hài lòng với một số offset_of<&S::x>::value (như phần còn lại của các đặc điểm kiểu), nhưng không thể tìm ra phép thuật cú pháp cho nó.

+0

Tôi đang cố gắng tìm ra nơi trong tiêu chuẩn mà nó nói rằng điều này làm những gì bạn mong đợi nó. Nhưng tôi không thể tìm thấy nó. –

+4

Có gì sai với tiêu chuẩn ['offsetof'] (http://en.cppreference.com/w/cpp/types/offsetof)? –

+0

@NicolBolas Tôi đoán là không. Không nên dereference của một 'nullptr' (và tôi nghĩ' -> 'đếm như dereference) được UB đã? Nhưng sau đó một lần nữa, phiên bản của vectơ 'offsetof' của VC không khác gì. Vì vậy, trong thực tế nó có thể là thực hiện khá định nghĩa hơn không xác định. –

Trả lời

13

Sau đây nên làm việc (các khoản tín dụng đi đến the answer to this question cho ý tưởng):

#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, ""); 

Lưu ý rằng trong gcc, các offsetof nở vĩ mô để một phần mở rộng được xây dựng trong có thể được sử dụng tại thời gian biên dịch (xem dưới đây). Ngoài ra, mã của bạn gọi UB, nó dereferences một con trỏ null, vì vậy ngay cả khi nó có thể làm việc trong thực tế, không có đảm bảo.

#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) 

Như đã chỉ ra bởi Luc Danton, biểu thức hằng số không thể liên quan đến một reinterpret_cast theo tiêu chuẩn C++ 11 mặc dù hiện gcc chấp nhận mã (xem bug report here). Ngoài ra, tôi đã tìm thấy defect report 1384 mà nói về việc đưa ra các quy tắc ít nghiêm ngặt hơn, vì vậy điều này có thể thay đổi trong tương lai.

+0

Có vẻ tốt với tôi, sau đó. – hvd

+4

Một biểu thức liên tục không thể liên quan đến 'reinterpret_cast' (trừ khi không được đánh giá). –

+1

@LucDanton: Cảm ơn thông tin. Tôi cũng tìm thấy [báo cáo lỗi 1384] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1384) nói về việc nới lỏng các hạn chế về điều đó. –

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