2013-02-13 27 views
16

Potrzebuję napisać funkcję adresową constexpr, ale uważam to za niemożliwe. Czy ktoś wie, czy to jest możliwe?Czy adresof() może zostać zaimplementowany jako funkcja constexpr?

implementacja referencyjna na cppreference.com:

template< class T > 
T* addressof(T& arg) 
{ 
    return reinterpret_cast<T*>(
      &const_cast<char&>(
       reinterpret_cast<const volatile char&>(arg) 
      ) 
     ); 
} 

wykorzystuje reinterpret_cast (podobnie do realizacji GCC), więc nie zrobi. Widzę, że najnowsza wersja C++ Standard, N3485 również nie wymaga, aby addressof() był constexpr pomimo, że wiele funkcji z nagłówka < zostało niedawno zaktualizowanych do constexpr.

Możliwe, choć nie bardzo przekonujący lub użyteczne use-case dla niego byłoby:

constexpr MyType m; 
constexpr MyType const* p = &m;   // this works today 
constexpr MyType const* p = addressof(m); // this is my question 

Wyobraźmy sobie, że MyType został przeciążony operator &.

+0

Mogę zapytać dlaczego jest to potrzebne? –

+4

Adresy obiektów nie są znane podczas kompilacji. – Erik

+0

To nie jest możliwe: rzuty dziwne nie mogą być "constexpr". Ale nie mogę sobie wyobrazić sytuacji, w której potrzebny byłby "adres constexpr". –

Odpowiedz

1

Jednym z częściowych obejść jest zdefiniowanie dowolnego takiego obiektu w opakowaniu union i przekazanie wskaźników do union. Wskaźnik do opakowania można łatwo przekonwertować na odniesienie do typu. Arytmetyka wskaźnikowa powinna działać na tablicach obwolut. Ale nadal nie widzę sposobu, aby uzyskać wskaźnik do typu z przeciążonym operator&.

Tylko jeden członek potrzebuje union; struct będzie działać w praktyce, ale teoretycznie implementacja może dodać dopełnienie na początku. Nie chodzi o to, że dopełnienie naprawdę robi różnicę, jeśli mimo to nie można uzyskać wartości stałej wyrażenia w postaci stałej.

template< typename t > 
union storage_wrapper 
    { t obj; }; 

constexpr storage_wrapper<MyType> m{{ args_to_MyType_constructor }}; 
constexpr storage_wrapper<MyType> *p = &m; // not overloaded 
8

Jak wspomniano w komentarzach, można wykryć, czy przeciążony operator& jest dostępna za pomocą SFINAE. I jak Potatoswatter zwraca uwagę w komentarzach, to trzeba mieć trzy oddzielne kontrole:

1) czy x.operator&() akceptuje

2) czy operator&(x) akceptuje

Pierwsze dwa są dwa sposoby użytkownik -provided operator& może być zdefiniowany.

3) czy &x akceptuje

Ta trzecia kontrola jest konieczna, ponieważ x.operator&() może zostać odrzucony, ponieważ operator& istnieje, ale jest prywatny. W takim przypadku &x jest nieprawidłowe.

Te kontrole można wprowadzić, sprawdzając sizeof(f(std::declval<T>())), gdzie f jest przeciążone w taki sposób, że typ zwracany zależy od tego, czy T przejdzie kontrolę.

namespace addressof_helper { 
    template <typename T> 
    static char (&checkaddressof(...))[1]; 

    template <typename T> 
    static char (&checkaddressof(T &&, typename std::remove_reference<decltype(&std::declval<T &>())>::type * = 0))[2]; 

    template <typename T> 
    static char (&checknonmember(...))[1]; 

    template <typename T> 
    static char (&checknonmember(T &&, typename std::remove_reference<decltype(operator&(std::declval<T &>()))>::type * = 0))[2]; 

    template <typename T> 
    static char (&checkmember(...))[1]; 

    template <typename T> 
    static char (&checkmember(T &&, typename std::remove_reference<decltype(std::declval<T &>().operator&())>::type * = 0))[2]; 
} 

Następnie można korzystać z tych funkcji pomocniczych na wybór realizacja addressof używać:

template <typename T> 
constexpr typename std::enable_if< 
    sizeof(addressof_helper::checkaddressof<T>(std::declval<T>())) == 2 
    && sizeof(addressof_helper::checknonmember<T>(std::declval<T>())) == 1 
    && sizeof(addressof_helper::checkmember<T>(std::declval<T>())) == 1, 
    T *>::type addressof(T &t) { 
    return &t; 
} 

template <typename T> 
/* no constexpr */ typename std::enable_if< 
    sizeof(addressof_helper::checkaddressof<T>(std::declval<T>())) == 1 
    || sizeof(addressof_helper::checknonmember<T>(std::declval<T>())) == 2 
    || sizeof(addressof_helper::checkmember<T>(std::declval<T>())) == 2, 
    T *>::type addressof(T &t) { 
    return reinterpret_cast<T *>(&const_cast<char &>(reinterpret_cast<const volatile char &>(t))); 
} 

Pozwala addressof być stosowane w stałych wyrażeniach tak długo, jak operator& nie jest przeciążony.Jeśli jest przeciążony, wydaje się, że nie ma sposobu, aby niezawodnie uzyskać adres w formie, która jest użyteczna w wyrażeniu stałym.

Należy pamiętać, że GCC 4.7 odrzuca zastosowania tych przypadków implementacji addressof, w których powinien działać. GCC 4.8 i wyższe działają, podobnie jak klang.

Użyłem pojedynczej implementacji addressof, która została przekierowana do funkcji pomocniczej we wcześniejszej wersji mojej odpowiedzi, ale niedawno uświadomiłem sobie, że to nie jest dobry pomysł, ponieważ może łatwo doprowadzić do naruszeń ODR, jeśli addressof<X> jest stosowany w niektórych jednostkach klasy X w wielu jednostkach tłumaczeniowych, w niektórych z nich zdefiniowano X, a w niektórych z nich X jest niekompletny. Posiadanie dwóch oddzielnych funkcji pozwala uniknąć tego problemu.

Jedynym pozostałym problemem jest to, że może zawieść jeśli addressof<X> jest używane w jednostce tłumaczeniowej przed definicją X niestandardowego operator&. Powinno to być na tyle rzadkie, że nie stanowi to problemu w praktyce.

testami dla sensownych przykładów:

class A { } a; 
class B { private: B *operator&(); } b; 
class C { C *operator&(); } c; 
class D { } d; 
D *operator&(D &); 
extern class E e; 

int main() { 
    constexpr A *pa = addressof(a); 
    /* no constexpr */ B *pb = addressof(b); 
    /* no constexpr */ C *pc = addressof(c); 
    /* no constexpr */ D *pd = addressof(d); 
    constexpr E *pe = addressof(e); 
} 

class E { } e; 
+0

SFINAE jest wadliwe. Nie przechwytuje przeciążenia członka nie będącego członkiem ani prywatnego. Zobacz mój komentarz. – Potatoswatter

+0

Wbudowany '&' operator nie jest dostępny, jeśli przeciążenie jest niedostępne. – Potatoswatter

+0

@Potatoswatter Zaktualizowany, czy to wygląda lepiej dla Ciebie? – hvd

Powiązane problemy