2013-02-27 10 views
5

Mam enum jak:Typ C++ 11 do mapowania wyliczeniowego?

enum E 
{ 
    TYPE_FLOAT, 
    TYPE_CHAR, 
    TYPE_INT 
} 

I chcę utworzyć odwzorowanie kompilacji, aby uzyskać odpowiednią E dla typu jak:

GetE<float> // returns TYPE_FLOAT 
GetE<char> // returns TYPE_CHAR 
GetE<int> // returns TYPE_INT 

myślałem o:

template<class T> struct GetE; 

template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; }; 
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; }; 
template<> struct GetE<int> { static constexpr E type = TYPE_INT; }; 

Ale pojawiają się błędy:

undefined reference to `GetE<int>::type' 

Jaki jest najlepszy sposób na zrobienie tego? I dlaczego błąd?

Odpowiedz

6

to zależy od sposobu korzystania z nich stałe wyrażenia.

ODR (zasada jednej definicji) stanowi

(§3.2/2) [...] Zmienna którego nazwa pojawia się jako potencjalnie z wyrażenia jest ODR wykorzystane, o ile nie jest obiekt, który spełnia wymagania dotyczące pojawiania się w stałym wyrażeniu (5.19), a konwersja l-wartość-r (4.1) jest natychmiast stosowana. [...]

(A potem, wiele specjalnych zasad, wyjątków i odstępstw od wyjątków naśladowania.)

Każda zmienna jest ODR-używany, musi mieć dokładnie jedną definicję. Twoje stałe wyrażenia mają deklarację, ale nie definicję, więc to dobrze, chyba że użyjesz odr z jednego z nich.

Na przykład, następujący idzie dobrze:

int main() { 
    E e = GetE<float>::type; 
    return 0; 
} 

Ale tego nie robi:

void f(const E &) 
{ } 

int main() { 
    f(GetE<float>::type); 
    return 0; 
} 

ponieważ f wymaga (const) referencję, więc RValue lwartość do konwersji nie może być zastosowane natychmiast, a więc stanowi to użycie odr. Kompilator będzie skarżył się, że pomija definicję.

(Uwaga: jak stwierdził ShafikYaghmour (patrz komentarze), możesz nie otrzymać skargi, jeśli kompilator używa optymalizacji, ponieważ referencje mogą zostać zoptymalizowane z dala. Aby odtworzyć skargę kompilatora, użyj flagi -O0 (lub podobnej, w zależności od kompilatora).)

Aby rozwiązać problem, wymaganą definicję można podać w zwykły sposób, tj.poza struct rozdzielczości:

constexpr E GetE<float>::type; 
constexpr E GetE<char>::type; 
constexpr E GetE<int>::type; 

Ale ponieważ to musiałoby się wydarzyć w .cpp (nie pliku nagłówka), będziesz skończyć się koniecznością utrzymania deklaracje i definicje w dwóch różnych miejscach, co jest niewygodny.

roztworze właśnie zasugerował w swoim komentarzu, to znaczy określić constexpr (inline) oraz funkcji, dźwięków prawej:

template <class T> constexpr E GetE(); 

template <> constexpr E GetE<float>() 
{ return TYPE_FLOAT; } 

template <> constexpr E GetE<char>() 
{ return TYPE_CHAR; } 

template <> constexpr E GetE<int>() 
{ return TYPE_INT; } 

void f(const E &) 
{ } 

int main() { 
    E e = GetE<float>(); 

    f(GetE<float>()); 

    return 0; 
} 
+1

Tak, to brzmi rozsądnie. Zmieniłem używanie szablonu funkcji, takiego jak 'tmpl E GetE()', a następnie specjalizowałem się, że zamiast tego to naprawiło. Dzięki. –

+1

@jogojapan Próbuję się nauczyć z twojego komentarza, ale nie widzę błędu przy użyciu ostatniego fragmentu kodu: http://liveworkspace.org/code/4oTEis Czego mi brakuje? Dziękujemy –

+1

@ShafikYaghmour Jest to spowodowane flagą kompilatora '-O2'. Optymalizuje odniesienia od siebie. Dobry komentarz, jednak wspomnę o tym w odpowiedzi. – jogojapan

1

Statyczne zmienne składowe muszą być zdefiniowane poza zakresem Klasa:

class C { 
    const static int x = 5; 
}; 

decltype(C::x) C::x; 
+0

nawet jeśli jego 'constexpr'? To jest dziwne. –

+0

Cóż, na GCC 4.7 to działa. Może to tylko twój kompilator? –

+0

z wykorzystaniem także 4.7.2. –

1

Może dlatego, że zapomniał umieścić średnik po definicji enum, to działa na mnie w LiveWorkSpace:

#include <iostream> 

enum E 
{ 
    TYPE_FLOAT, 
    TYPE_CHAR, 
    TYPE_INT 
} ; 

template<class T> struct GetE; 

template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; }; 
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; }; 
template<> struct GetE<int> { static constexpr E type = TYPE_INT; }; 

int main() 
{ 
    std::cout << GetE<int>::type << std::endl ; 
} 

tu jest link do kodu http://liveworkspace.org/code/nHqUe $ 6

+0

nie w prawdziwym kodzie Mam średnik. Hmmm dziwne. Używam także wersji 4.7.2. Nie wiem, co się dzieje. Dzięki. –