2012-10-10 13 views
14

Mam potrzebę użycia offsetof z template za pomocą selektora elementów. Mam wymyślić sposób, jeśli będziesz wybaczyć niezręczną składnię:C++ Przesunięcie czasu kompilacji wewnątrz szablonu

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

Wykorzystanie nie jest doskonały (irytujące w najlepszym razie):

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

Niezasychające constexpr forma jest łatwiejsza używać:

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

z powodu oczywistej wady, że nie odbywa się w czasie kompilacji (lecz łatwiej wykorzystać)

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

Czego szukam, to składnia podobna do odmiany innej niż constexpr, ale wciąż pełna kompilacja; jednak nie mogę wymyślić dla niego składni. Byłbym też zadowolony z offset_of<&S::x>::value (podobnie jak pozostałe cechy typu), ale nie mogę znaleźć dla niego magii składni.

+0

Próbuję dowiedzieć się, gdzie w standardzie, że mówi, że robi to, czego oczekujesz. Ale nie mogę tego znaleźć. –

+4

Co jest nie tak ze standardem ["offsetof"] (http://en.cppreference.com/w/cpp/types/offsetof)? –

+0

@NicolBolas Przypuszczam, że tak nie jest. Czy nie powinno być już wyodrębnianie "nullptr" (i myślę, że '->" liczy się jako dereferencja)? Ale znowu, wersja VC 'offof' makro nie jest inna. Tak więc w praktyce jest to prawdopodobnie raczej definicja zdefiniowana niż niezdefiniowana. –

Odpowiedz

13

Poniższa powinien działać (kredyty przejść do the answer to this question za pomysł):

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

pamiętać, że w GCC, offsetof makro rozszerza się do wbudowanego rozszerzenia, które mogą być użyte w czasie kompilacji (patrz poniżej). Twój kod wywołuje także UB, odrzuca zerowy wskaźnik, więc nawet jeśli może działać w praktyce, nie ma żadnych gwarancji.

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

Jak wskazano Luc Danton, wyrażeniami nie może wiązać się z reinterpret_cast według normy C++ 11 chociaż obecnie GCC przyjmuje kodu (patrz bug report here). Ponadto znalazłem defect report 1384, który mówi o tym, że przepisy są mniej surowe, więc może się to zmienić w przyszłości.

+0

Wygląda więc dobrze dla mnie. – hvd

+4

Stałe wyrażenie nie może zawierać "reinterpret_cast" (chyba że nie zostało to ocenione). –

+1

@LucDanton: Dzięki za informację. Znalazłem także [raport o defektach 1384] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1384), który mówi o rozluźnieniu ograniczeń dotyczących tego. –

Powiązane problemy