2014-10-04 29 views
7

Jak to się stało, że następujące prace nad gcc ale nie na clang (see it live):Statyczny constexpr odr-używane czy nie?

constexpr int giveMeValue() { return 42; } 

struct TryMe { 
    static constexpr int arr[1] = { 
     giveMeValue() 
    }; 
}; 

int main() { 
    int val = TryMe::arr[0]; 
    return val; 
} 

dostaję nierozwiązane zewnętrznych symbolu z brzękiem.

Czy obiekt jest TryMe::arr[0]? Jeśli tak, to czy jest używany odr?

+1

Która wersja clang? –

+0

Ten na coliru – Dean

+0

Działa dobrze dla mnie: http://coliru.stacked-crooked.com/a/2b319b9351784244 Czy włączono 'flaga C++ 11'? – texasbruce

Odpowiedz

7

TryMe::arr jest odr-used ale nie zawiera definicji (see it live):

constexpr int TryMe::arr[1]; 

Dlaczego wynik niezgodny między gcc i clang? To dlatego, że naruszanie ODR nie wymagają disagnostic, zarówno z projektu standardu C++ 11 i C++ 14 (nacisk kopalni):

Każdy program zawiera dokładnie jedną definicję każdej non-inline funkcja lub zmienna, która jest odr używana w tym programie; brak diagnostyki wymagane.

Możemy to zobaczyć jest odr używane z projektu C++ standardowe 11, sekcja 3.2 który mówi:

Wyrażenie jest potencjalnie ocenić, chyba że jest to unevaluated operand (punkt 5) lub jego podwyrażenie. Zmienna, której nazwa pojawia się jako potencjalnie oszacowany wyrażenie, jest używana odr, chyba że jest to obiekt spełniający wymagania dla pojawiania się w stałej ekspresji (5.19), a konwersja l-wartość-do-r (4.1) jest natychmiastowa. stosowany.

TryMe::arr Przedmiotem i spełnia wymagania na pojawiające się w stałej ekspresji, ale konwersja lwartość do RValue nie jest natychmiast stosowany do TryMe::arr ale TryMe::arr[0].

Zaktualizowana treść od standardu w Projekty C++ 14, który odnosi się do C++ 11, a ponieważ była ona stosowana poprzez raportu defektami (DR 712):

zmiennej x, której nazwisko pojawia się jako potencjalnie oszacowaną ekspresję jest odr-stosowane, chyba że zastosowanie konwersji wartości l do (r) (4.1) na x daje stałą ekspresję (5.19), która nie wywołuje żadnych niebanalnych funkcji i, jeśli x jest object, ex jest elementem zestawu potencjalnych wyników wyrażenia e, gdzie albo lwartość do RValue konwersji (4.1) stosuje się do E lub E jest wyrażeniem odrzucono wartość

W potencjalne wyniki ekspresji TryMe::arr[0] jest pusta kryteriami w 3.2 ust 2 itd jest używany odr.

Uwaga: należy podać definicję poza klasą, jak na odcinku 9.4.2[class.static.data] który mówi (podkr):

Statyczny członek danych dosłowny typ można zadeklarować w definicji klasy ze specyfikatorem constexpr; jeśli tak, jego deklaracja powinna określać inicjator klamrowy lub równy, w którym każda inicjująca klauzula , która jest ekspresją przypisania, jest wyrażeniem stałym. [Uwaga: W obu tych przypadkach element może pojawiać się w wyrażeniach stałych. -end uwaga] Członek zostaje jeszcze zdefiniowane w zakresie przestrzeni nazw, jeśli jest odr wykorzystane (3.2) w programie oraz definicję zakresu nazw nie zawierają inicjator

Aktualizacja

TC wskazał defect report 1926 który dodaje się następujące pocisk 3.2 [basic.def.odr] Punkt 2:

  • gdy E oznacza działanie indeksowanie (5.2.1 [expr.sub]) o operandzie tablicy, zestaw zawiera ten operand.

Czyli indeksowanie tablicy nie jest już ODR-use a więc kod OP będzie dobrze uformowane w C++ 1z i wydaje się, że C++ 14, ponieważ wada to wygląda przeciwko C++ 14.

+0

Jeszcze jedno wyjaśnienie dotyczące deklaracji a definicji. To, co OP ma w swojej strukturze "TryMe", może wydawać się definicją, ale w rzeczywistości jest deklaracją ze względu na zabawne reguły dotyczące tego, kiedy deklaracja jest również definicją.Tutaj OP ma zmienną 'statyczną' z inicjatorem w klasie, który jest zwykle niedozwolony, ale jest tu dozwolony z powodu' constexpr' (i 'int'). Jednak OP faktycznie nie przydzielił pamięci dla 'TryMe :: arr', ponieważ zmienna jest również" statyczna ", która wymaga pamięci zewnętrznej. Tak więc, jak każda zmienna 'statyczna', OP nadal musi dostarczyć deklarację poza strukturą. – AndyG

+0

(Kontynuując poprzedni komentarz, ponieważ zabrakło mi miejsca). Jednak nie jest dobrym pomysłem podanie definicji "constexpr int TryMe :: arr [1];" w tym samym pliku, który OP definiuje strukturę 'TryMe', ponieważ każda klasa, która chce dołączyć definicję struct' TryMe' będzie następnie spróbuj alokować pamięć dla 'TryMe :: arr [1]', naruszając regułę jednej definicji. Więc zwykle dzieje się tak, że wstawisz 'constexpr int TryMe :: arr [1]' w jakiś plik .cpp, oddzielny od nagłówka. – AndyG

+0

@AndyG punkt fair, założyłem pewną wiedzę, powinienem był dodać więcej szczegółów. Pozwól mi dodać więcej szczegółów. –

Powiązane problemy