2017-06-28 28 views
14

Rozważmy poniższy kod:Czy `std :: array <T, 0>` domyślnie konstruuje gdzie `T` nie jest domyślnie konstruktywne?

#include <array> 

struct T 
{ 
    T() = delete; 
}; 

int main() 
{ 
    std::array<T, 0> a; 
    a.size(); 
} 

My brak zainicjować 0-wielkości tablicy. Ponieważ nie ma elementów, nie powinien być wywoływany żaden konstruktor z T.

Jednak nadal wymaga Clang, aby był domyślnie konstruktywny, a GCC akceptuje powyższy kod.

Zauważ, że jeśli zmienimy inicjalizacji tablicy do:

std::array<T, 0> a{}; 

Clang akceptuje to w ten czas.

Czy domyślnie konstruktywna T uniemożliwia zachowanie domyślnej wartości domyślnej ?

+2

Inicjowanie agregacji jest poprawne, nawet jeśli istnieją usunięte domyślne konstruktory. Ale inicjowanie domyślne (iirc) nie jest. Myślę, że zachowanie klangów jest rozsądne. Wykonywanie różnych czynności nie może mieć wpływu na to, czy kod jest prawidłowy podczas kompilacji. –

+1

@ JohannesSchaub-litb Czy spodziewasz się, że element zostanie zainicjowany na macierz o rozmiarze 0? Clang robi [to] (https://wandbox.org/permlink/LashAGkeVwnfsodC), co wydaje mi się szalone. Jeśli nie ma elementów, nie należy inicjować żadnych elementów. – Jamboree

+2

Patrz [LWG 2157] (https://timsong-cpp.github.io/lwg-issues/2157). –

Odpowiedz

3

Dzięki @ T.C., Jak wskazano w jego comment, jest on adresowany pod numerem LWG 2157, który nadal jest problemem otwartym w chwili pisania tego tekstu.

Proponowana uchwała dodaje ten podpunkt (kopalnia nacisk):

nieokreślony wewnętrzna struktura tablicy w tym przypadku zezwalają inicjowania takich jak:

array<T, 0> a = { }; 

a wspomniane pliki uruchamiania musi być ważny nawet gdy T nie jest domyślnie konstruktywnym.

Jest więc oczywiste, że zamierzonym zachowaniem jest domyślna konstrukcja, nawet gdy T nie jest.

+0

Jest jasne, że to zachowanie zamierzone przez wnioskodawcę (i przez JW) teraz, ale niekoniecznie przez kogokolwiek innego i niekoniecznie w tym czasie, gdy pisano/wydano wersje kompilatora! Rzeczywiście, tekst, który łączysz, sugeruje, że pierwotny zamiar był dosłownie przeciwny. To nadal jest najlepsza odpowiedź. –

2

To pytanie wyjaśnia, co dzieje się z brzękiem i std::arrayDeleted default constructor. Objects can still be created... sometimes

Ale z gcc różnica pochodzi z kodem biblioteki. Jest rzeczywiście specyficzny szczegół implementacji w kodzie gcc, które są istotne dla tej kwestii jako @StoryTeller mentioned

gcc ma szczególny przypadek dla std::array o rozmiarze 0, zobacz następujący kod z ich <array> nagłówka (z gcc 5.4.0)

template<typename _Tp, std::size_t _Nm> 
struct __array_traits 
{ 
    typedef _Tp _Type[_Nm]; 

    static constexpr _Tp& 
    _S_ref(const _Type& __t, std::size_t __n) noexcept 
    { return const_cast<_Tp&>(__t[__n]); } 

    static constexpr _Tp* 
    _S_ptr(const _Type& __t) noexcept 
    { return const_cast<_Tp*>(__t); } 
}; 

template<typename _Tp> 
struct __array_traits<_Tp, 0> 
{ 
struct _Type { }; 

static constexpr _Tp& 
_S_ref(const _Type&, std::size_t) noexcept 
{ return *static_cast<_Tp*>(nullptr); } 

static constexpr _Tp* 
_S_ptr(const _Type&) noexcept 
{ return nullptr; } 
}; 

jak widać, istnieje specjalizacja __array_traits (który jest używany w std::array dla bazowego tablicy), gdy rozmiar tablicy jest 0, to nie ma nawet tablicę typu to matrycy dalej. Typ _Type nie jest tablicą, ale pustą strukturą!

Dlatego nie wywołuje się konstruktorów.

5

Ponieważ nie ma elementów, nie należy wywoływać żadnego konstruktora T.
Czy domyślne ustawienie T uniemożliwia zachowanie domyślnej wartości domyślnej std::array<T, 0>?

Standard nie określa, jaki układ powinien mieć dla nas odpowiedź na to pytanie. Wymiarach kierunek zerowego Tablica mówi tylko zachowuje się jak następuje:

[array.zero]

1 tablicą zapewnia wsparcie dla szczególnego przypadku N == 0
2 W przypadku, gdy N == 0 begin() == end() == unikalna wartość. Zwracana wartość danych() jest nieokreślona.
3 Efekt wywołania funkcji front() lub back() dla tablicy o rozmiarze zerowym jest niezdefiniowany.
4 Funkcja swap() musi mieć specyfikację wyjątku dotyczącego braku wyrzucania.

Opisane zachowanie wynika prawdopodobnie z różnic w samej implementacji.

+0

Nie chodzi tylko o układ. Implementacja Clanga faktycznie inicjuje jeden element dla tablicy o rozmiarze 0 w środowisku wykonawczym. – Jamboree

+1

@Jamboree - Ponieważ nie są związane z żadnym określonym układem. Ich przywilej w ramach standardu. – StoryTeller

+1

Jeśli argumentem jest "ta lista nie zabrania tworzenia elementu, więc tworzenie elementu jest dozwolone", czy zgadzasz się z implementacją, która wywołuje 'std :: exit()' dla 'array a;'? Nie rozumiem jeszcze argumentu, jak sądzę. –