2010-11-17 14 views
10

Niedawno odkryłem irytujący problem w dużym programie, który rozwijam; chciałbym zrozumieć, jak to naprawić w najlepszy sposób. Przeciąłem kod do następującego minimalnego przykładu.Stałe całki C++ + operator wyboru = problem!

#include <iostream> 
using std::cin; 
using std::cout; 

class MagicNumbers 
{ 
public: 
    static const int BIG = 100; 
    static const int SMALL = 10; 
}; 

int main() 
{ 
    int choice; 
    cout << "How much stuff do you want?\n"; 
    cin >> choice; 
    int stuff = (choice < 20) ? MagicNumbers::SMALL : MagicNumbers::BIG; // PROBLEM! 
    cout << "You got " << stuff << "\n"; 
    return 0; 
} 

otrzymuję błędy ogniwem w gcc 4.1.2 przy kompilacji z -O0 lub -O1 ale wszystko jest OK podczas kompilacji z -O2 lub -O3. Dobrze łączy się przy użyciu MS Visual Studio 2005, niezależnie od opcji optymalizacji.

test.cpp:(.text+0xab): undefined reference to `MagicNumbers::SMALL'

test.cpp:(.text+0xb3): undefined reference to `MagicNumbers::BIG'

I spojrzał pośredniego kodu maszynowego, i tak, nieoptymalizowanym kod uważane małe i duże jak zewnętrzne zmienne int, a optymalna stosowanej rzeczywistych liczb. Każdy z następującymi zmianami rozwiązuje problemu:

  • Używaj enum zamiast int dla stałych: enum {SMALL = 10}

  • oddanych stała (jednego) przy każdym użyciu: (int)MagicNumbers::SMALL lub (int)MagicNumbers::BIG lub nawet MagicNumbers::SMALL + 0

  • użyć makra: #define SMALL 10

  • nie użyć operatora wyboru: if (choice < 20) stuff = MagicNumbers::SMALL; else stuff = MagicNumbers::BIG;

Lubię pierwszą opcję najlepiej (ale to nie jest idealny, ponieważ faktycznie używamy uint32_t zamiast int dla tych stałych i enum jest synonimem int). Ale naprawdę chcę zapytać: czyj to jest błąd?

Czy jestem winnym za niezrozumienie, jak działają statyczne stałe całkowe?

Czy powinienem obwiniać gcc i mam nadzieję na poprawkę (czy może najnowsza wersja ma już poprawkę, czy może jest niejasny argument wiersza poleceń, aby to działało)?

Tymczasem, po prostu skompilować mój kod z optymalizacji, i jest to ból do debugowania: -O3

Odpowiedz

7

Pomimo konwencjonalnej porady, stwierdziłem, że static const int ... niezmiennie daje mi więcej bólu głowy niż dobry stary enum { BIG = 100, SMALL = 10 };. A z C++ 11 dostarczającym mocno napisanych wyrażeń, mam teraz jeszcze mniej powodów, aby używać static const int ....

+0

Mocno napisane wyrazy? Chłodny! – dreamlax

+0

To wciąż "C++ 0x" .. BS mówi, że "x" jest szesnastkowo. :) –

+0

Czy mamy wziąć zakłady na to? ;-) –

1

będę ciężko stwierdzić, że jest to niczyją bug.

Statyczne całki stałe podane w punkcie deklaracji nie są zmiennymi, są wyrażeniami stałymi. Aby istniała zmienna, musisz ją jeszcze zdefiniować.

Zasady obowiązujące operatora trójskładnikowego są dość absurdalnie złożone, prawdopodobnie tak koniecznie, i tak naprawdę nie mówią nic o stałych wyrażeniach, tylko wartościach r; oczywiście kompilator myśli, że powinny to być zmienne, chyba że optymalizacja jest uruchamiana. Myślę, że można dowolnie interpretować wyrażenie (jako wyrażenie stałe lub zmienne).

7

statycznymi członkami danych don't work like that w C++:

Static data members are not part of objects of a given class type; they are separate objects. As a result, the declaration of a static data member is not considered a definition. The data member is declared in class scope, but definition is performed at file scope. These static members have external linkage.

Ty deklarujące tylko te stałe, mimo że je inicjalizacji.Trzeba jeszcze określić nich w zakresie przestrzeni nazw:

class MagicNumbers 
{ 
public: 
    static const int BIG = 100; 
    static const int SMALL = 10; 
}; 

const int MagicNumbers::BIG; 
const int MagicNumbers::SMALL; 

To będzie pozbyć się błędów łącza.

+3

Ja * myślę, że to jest złe. Zbieram ... –

+0

@John, faktycznie to testowałem, wiesz;) –

+0

@John, która część myślisz, że jest zła? Nie chodzi o to, jak rozwiązałbym problem, "zakres pliku" jest w pewnym sensie niewłaściwym określeniem, i tak naprawdę nie mówi, dlaczego użycie trójskładnikowego nie działa, ale jeśli/else to robi ... ale ma rację. –

1

Jestem nowym do C++, ale myślę, że deklaracja klasy deklaruje jedynie, że te statyczne członkowie istnieje, trzeba jeszcze zdefiniować je gdzieś:

class MagicNumbers 
{ 
public: 
    static const int BIG; 
    static const int SMALL; 
}; 

const int MagicNumbers::BIG = 100; 
const int MagicNumbers::SMALL = 10; 

Wyższe poziomy optymalizacji prawdopodobnie obejmują poziom analiza statyczna dostatecznie dokładna, aby ustalić, że można wymieniać wartości z rzeczywistymi wartościami i nie dawać im rzeczywistego zapisu (semantyka będzie taka sama), więc zdefiniowanie tych zmiennych w takich okolicznościach byłoby zbędne, a więc łączy się OK.

0

Trzeba jeszcze przydzielić miejsca dla nich gdzieś:

class MagicNumbers 
{ 
public: 
    static const int BIG = 100; 
    static const int SMALL = 10; 
}; 
const int MagicNumbers::BIG; 
const int MagicNumbers::SMALL; 
3

Heh, w zależności od standardu C++, 9.4.2 (class.static.data):

If a static data member is of const literal type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

So deklarację jest poprawna, ale musisz mieć gdzieś definicję. Zawsze myślałem, że umiesz zdyscyplinować definicję, ale przypuszczam, że nie jest to zgodne z normą.

+0

Standardowo nie można ich zdefiniować ... czasami termin "używany" jest zdefiniowany w normie dla bardzo konkretnego znaczenia. Zobacz odpowiedź litb dla odniesienia do odpowiedniej części standardowej i raportu o usterce w odniesieniu do tego konkretnego zastosowania. –

+0

@ Matthieu, słodko! Uwielbiam stackoverflow, jeśli tylko dlatego, że litb wie więcej o C++ niż ja. – MSN

+0

Facet jest naprawdę imponujący, sam jestem fanem jego szablonów "hack" :) –

20

This is a known issue. Standard jest winien, a Ty nie podajesz definicji statyki. W zależności od twojego punktu widzenia:

+0

+1 dzięki za referencję. Chyba powinienem przeczytać raporty o błędach. :) –

+0

To powinna być wybrana odpowiedź: \ –

+0

@Noah: To zależy od tego, czy chcesz zrozumieć problem, czy go naprawić. FWIW, przegłosowałem tę odpowiedź. –

0

Dlaczego twoje magiczne liczby znajdują się w klasie?

namespace MagicNumbers { 
    const int BIG = 100; 
    const int SMALL = 10; 
} 

Problem rozwiązany bez obawy o wady w standardzie C++.