2014-06-10 41 views
37

Rozważmy następujący fragment kodu, który jest w pełni do zaakceptowania przez kompilator C++ 11:Czy istnieje przyczyna zerowej wielkości std :: array w C++ 11?

#include <array> 
#include <iostream> 

auto main() -> int { 
    std::array<double, 0> A; 

    for(auto i : A) std::cout << i << std::endl; 

    return 0; 
} 

Zgodnie z normą § 23.3.2.8 [zerowej wielkości tablice]:

1 Array zapewnia obsługę specjalnego przypadku N == 0.

2 W przypadku, gdy N == 0, begin() == end() == wartość unikalna. Wartość zwracana
data() jest nieokreślona.

3 Efekt dzwonienia pod numer front() lub back() w przypadku macierzy o rozmiarze zerowym jest nieskończony.

4 Funkcja członka swap() będzie miała specyfikację noexcept, która jest równoważna z noexcept(true).

Jak wyświetlane powyżej, zero wielkości std::array s są całkowicie dopuszczalne w C++ 11, w przeciwieństwie do zera tablic wielkości (np int A[0];), gdzie są wyraźnie zabronione, ale są one dopuszczone przez niektóre kompilatory (np GCC) w kosztach niezdefiniowanego zachowania.

Biorąc pod uwagę ten "sprzeczność", mam następujące pytania:

  • Dlaczego C++ komisja postanowiła zezwolić zerowej wielkości std::array s?

  • Czy są jakieś cenne zastosowania?

+18

Prawdopodobnie będzie to zgodne z pojemnikami i ułatwi wykonywanie niektórych ogólnych rzeczy. – chris

+1

Jedna uwaga może być taka, że ​​nawet jeśli dowolne implementacje mają trudności z macierzami o zerowej wielkości, nie powinny mieć trudności z częściową specjalizacją dla 'std :: array '. – hvd

+2

Meta-programy szablonów są często rekursywne i często najprościej jest mieć dno rekursji na poziomie 0 zamiast 1 ... Ale przyznaję, że mam problem z konstruowaniem realistycznego przykładu w tym przypadku. – Nemo

Odpowiedz

40

Jeśli masz funkcję ogólną, jest ona zła, jeśli funkcja losowo zrywa specjalne parametry. Na przykład, powiedzmy, że można mieć funkcję szablonu, że trwa N losowe elementy tworzą Vector:

template<typename T, size_t N> 
std::array<T, N> choose(const std::vector<T> &v) { 
    ... 
} 

Nic nie zyskuje, jeśli powoduje to zachowanie niezdefiniowane lub błędów kompilatora jeśli N jakiegoś powodu okazuje się być zero.

W przypadku tablic surowych powodem ograniczenia jest brak typów z sizeof T == 0, co prowadzi do dziwnych efektów w połączeniu z arytmetyką wskaźników. Tablica z zerowymi elementami miałaby rozmiar zero, jeśli nie dodasz do niej żadnych specjalnych reguł.

Ale std::array<> jest klasą, a zajęcia zawsze mają rozmiar > 0. Więc nie napotkasz tych problemów z std::array<> i spójny interfejs bez arbitralnego ograniczenia parametru szablonu jest korzystne.

4

Jednym z zastosowań, o którym mogę myśleć, jest możliwość powrotu macierzy o zerowej długości i możliwość sprawdzenia funkcjonalności.

Na przykład patrz dokumentacja funkcji std::arrayempty(). Posiada następujące wartości zwracanej:

true if the array size is 0, false otherwise. 

http://www.cplusplus.com/reference/array/array/empty/

Myślę, że zdolność do powrotu i sprawdzić 0 Długość tablic jest zgodne ze standardem dla innych implementacjach typów STL, dla np. Wektory i mapy są zatem przydatne.

+11

Proszę nie odsyłać do cplusplus.com. Większość wpisów jest niekompletna lub błędna. Zamiast tego użyj http://en.cppreference.com/w/cpp/container/array. – kay

+4

@Kay Ale czy nie w tym przypadku? Również kiedy jest niekompletna lub zła? –

+0

@Kay Widzę to od czasu do czasu niekompletne na bardziej niejasne rzeczy - ale źle? Gdy? – druckermanly

1

Podobnie jak w przypadku innych klas kontenera, przydatne jest posiadanie obiektu, który reprezentuje wiele różnych elementów, oraz możliwość, aby tablica ta stała się pusta. Gdyby nie było to możliwe, należałoby stworzyć inny obiekt lub klasę zarządzającą, aby reprezentować ten stan w sposób legalny. Posiadanie tej umiejętności zawartej we wszystkich klasach kontenerów jest bardzo pomocne. Używając go, trzeba mieć nawyk związany z tablicą jako kontenerem, który może być pusty, oraz sprawdzanie rozmiaru lub indeksu przed odniesieniem do członka w przypadku, gdy nie wskazuje niczego.

1

W rzeczywistości jest kilka przypadków, w których chcesz to zrobić. Jest obecny również w wielu innych językach. Na przykład Java ma w rzeczywistości Collections.emptyList(), która zwraca listę, która ma nie tylko rozmiar zero, ale nie może być rozszerzona, zmieniona ani zmodyfikowana.

Przykład użycia może być, jeśli masz klasę reprezentującą autobus i listę pasażerów w tej klasie. Lista może być leniwy, zainicjowana, tylko utworzona, gdy pasażerowie wchodzą na pokład. Jeśli ktoś zadzwoni pod numer getPassengers(), to zamiast tego może utworzyć pustą listę zamiast tworzyć nową listę za każdym razem, aby raport był pusty.

Powracająca wartość zerowa również wpłynie na wewnętrzną efektywność klasy - ale wtedy życie stanie się o wiele bardziej skomplikowana dla wszystkich korzystających z klasy, ponieważ za każdym razem, gdy zadzwonisz pod numer getPassengers(), musisz zerować wynik. Zamiast tego, jeśli dostaniesz pustą listę z powrotem, tak długo, jak twój kod nie przyjmuje założeń, że lista nie jest pusta, nie potrzebujesz żadnego specjalnego kodu, aby obsłużyć go jako null.