2012-10-10 8 views
14

Tengo la necesidad de utilizar offsetof desde un template con un selector de miembros. Yo he llegado con una forma, si me disculpa la sintaxis incómoda:C++ Tiempo de compilación offsetof dentro de una plantilla

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

uso no es perfecto (molesto en el mejor):

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

La forma no es fácil constexpr de usar:

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

en la desventaja obvia de que no se realiza en tiempo de compilación (pero más fácil de usar):

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

Lo que estoy buscando es la sintaxis como la variedad no constexpr, pero aún completamente tiempo de compilación; sin embargo, no puedo encontrar la sintaxis para ello. También estaría contento con un offset_of<&S::x>::value (como el resto de los rasgos de tipo), pero no puedo entender la sintaxis mágica para él.

+0

Estoy tratando de averiguar en qué parte del estándar dice que hace lo que espera que haga. Pero no puedo encontrarlo –

+4

¿Qué pasa con el estándar ['offsetof'] (http://en.cppreference.com/w/cpp/types/offsetof)? –

+0

@NicolBolas Supongo que no. ¿No debería la eliminación de un 'nullptr' (y creo que' -> 'cuenta como desreferencia) ser UB ya? Pero, de nuevo, la versión de VC de la macro 'offsetof' no es diferente. Entonces, en la práctica, es más bien una implementación definida que indefinida. –

Respuesta

13

Lo siguiente debe trabajar (créditos van a the answer to this question de la idea):

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

Tenga en cuenta que en las CGC, el offsetof expande macro a una extensión orden interna que se pueden utilizar en tiempo de compilación (véase más adelante). Además, su código invoca UB, elimina la referencia de un puntero nulo, por lo que incluso si funciona en la práctica, no hay garantías.

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

como señaló Luc Danton, expresiones constantes no pueden implicar una reinterpret_cast según el estándar C++ 11 aunque en la actualidad gcc acepta el código (véase la bug report here). Además, encontré defect report 1384 que habla sobre hacer las reglas menos estrictas, por lo que esto podría cambiar en el futuro.

+0

me parece bien, entonces. – hvd

+4

Una expresión constante no puede incluir un 'reinterpret_cast' (a menos que no se evalúe). –

+1

@LucDanton: Gracias por la información. También encontré un [informe de defectos 1384] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1384) que habla de aflojar las restricciones al respecto. –

Cuestiones relacionadas