Benutzerdefinierte Reihungen in C++ (Benutzerdefinierte Reihungen in C++), Information, Seite 724204
https://www.purl.org/stefan_ram/pub/array_template_c++ (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram
C++-Kurs

Abfolgen in C++ 

Deklaration einer Abfolge

Das folgende Programm deklariert den Namen »a« für ein Objekt der Klasse »::std::array< int, 3 >«.

Dieses Objekt enthält »3« Objekte, deren jedes den Datentyp »int« hat.

main.cpp

#include <array>
#include <initializer_list>

int main()
{ ::std::array< int, 3 >a{ { 4, 2, 7 } }; }

Protokoll
(keine Ausgabe)

Die Objekte eines mit der Typschablone (Lektion 28.6.) »::std::array« konstruierten Typs bestehen aus einer Abfolge  von Objekten des gleichen Typs. Daher nennen wir solch ein Objekt eine Abfolge.

Das erste  Argument der Typschablone (oben »int«) gibt den Basistyp  der Abfolge, es handelt sich um den Typ der in der Abfolge enthaltenen Objekte. Da wir jene Objekt auch als Komponenten der Abfolge bezeichnen, nennen wir deren Typ auch Komponententyp.

Das zweite  Argument der Typschablone (oben »3«) gibt die Anzahl  der Komponenten der Abfolge an. Diese Zahl wird auch als die Größe  der Abfolge bezeichnet.

Es folgt »a« als Name des Objekts.

Die Initialisierungsliste »{ { 4, 2, 7 } }« wird hier für eine sogenannte Aggregatinitialisierung  verwendet. Dadurch können einfach Objekte initialisiert werden, die hierfür im wesentlich als eine Abfolge anderer Objekte angesehen werden.

Die Verschachtelung zwei Klammerpaare erklärt sich so: Eine Abfolge ist ein Aggregat, das – wenn man es ganz genau betrachtet – seine Komponenten nicht direkt enthält. Vielmehr enthält es stets nur eine Komponente, eine sogenannte Reihung  (Lektion 23.6.) als „Unteraggregat“; jene Reihung enthält dann erst die eigentlichen Komponenten. Die Initialisierungsliste »{ { 4, 2, 7 } }« aggregatinitialisiert also die Abfolge (äußere Klammern) mit einem Aggregatinitialisierer für ihre einzige Komponten (innere Klammern), die enthaltene Reihung.

Das Objekt »a« enthält nach der Initialisierung also eine Reihung, welche die drei Werte »4«, »2« und »7« enthält. Normalerweise ignoriert man jene Reihung aber, und tut so, als ob die Werte direkt  in der Abfolge enthalten seien, falls es keinen bestimmten Grund dafür gibt, die Reihung hervorzuheben.

Ein Objekt der Klasse »::std::array< int, 3 >«
.---.---.---.
| 4 | 2 | 7 |
'---'---'---'

Die Deklaration endet schließlich mit einem Semikolon.

Die Elision geschweifter Klammern

Wenn der Initialisierer für ein Unteraggregat mit einer geschweiften Klammer beginnt, wie mit der zweiten geschweiften Klammer auf in »{ { 4, 2, 7 } }«, dann leitet diese geschweifte Klammer auf, wie behandelt, den Initialisierer für das Unteraggregat ein.

Es ist jedoch auch erlaubt, auf jene geschweifte Klammer zu verzichten und statt dessen »{ 4, 2, 7 }« zu verwenden (Elision eines Paares geschweifter Klammern). In diesem Falle werden dann so viele der folgenden Initialisierer wie nötig zur Initialisierung des Unteraggregats verwendet (hier die drei Initialisierer »4«, »2« und »7«).

Daher wird durch das folgende Programm dieselbe  Abfolge angelegt wie durch das vorige Programm.

main.cpp

#include <array>
#include <initializer_list>

int main()
{ ::std::array< int, 3 >a{ 4, 2, 7 }; }

Protokoll
(keine Ausgabe)

Falls ein Unterobjekt mit einer leeren oder unvollständigen Initialisierungsliste initialisiert werden soll, ist die Elision der Klammern jedoch in manchen Fällen nicht möglich, und die ausführlichere Schreibweise muß verwendet werden.

Einige Stilratgeber empfehlen, die Klammern, nicht wegzulassen (vermutlich, weil dies klarer ist).

Quellen *: 2018: 11.6.1 [dcl.init.aggr] p15

Die Herleitung der Typargumenten einer Klassenschablone

Falls die Typargumente weggelassen  werden, so kann die Implementation sie in vielen Fällen herleiten, in denen bestimmte Typargumente durch den Initialisierer auf einfache und direkte Weise nahegelegt werden: Da die Initialisierungsliste »{ 4, 2, 7 }« drei  int-Ausdrücke enthält, ist dies im Falle des folgenden Programms möglich.

main.cpp

#include <array>
#include <initializer_list>

int main()
{ ::std::array a{ 4, 2, 7 }; }

Protokoll
(keine Ausgabe)
main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <string> /* ::std::basic_string */

int main()
{ { ::std::string s{ 'a', 'b' };
::std::cout << s.size() << '\n';
::std::cout << s.at( 0 )<< '\n';
::std::cout << s.at( 1 )<< '\n'; }

{ ::std::basic_string b{ 'a', 'b' };
::std::cout << b.size() << '\n';
::std::cout << b.at( 0 )<< '\n';
::std::cout << b.at( 1 )<< '\n'; }

{ ::std::array v{ 'a', 'b' };
::std::cout << v.size() << '\n';
::std::cout << v.at( 0 )<< '\n';
::std::cout << v.at( 1 )<< '\n'; }}

Protokoll

2

a

b

2

a

b

2

a

b

Quellen *: 2018: 16.3.1.8 Class template argument deduction [over.match.class.deduct]

Der Zugriff auf die Komponenten

main.cpp

#include <array>
#include <iostream>
#include <ostream>
#include <initializer_list>

int main()
{ ::std::array const a{ 4, 2, 7 };
::std::cout << a[ 0 ]<< '\n';
::std::cout << a[ 1 ]<< '\n';
::std::cout << a[ 2 ]<< '\n'; }

Protokoll

4

2

7

Zugriffe mit undefiniertem Verhalten

main.cpp

#include <array>
#include <initializer_list>

int main()
{ ::std::array const a{ 4, 2, 7 };
a[ -1 ];
a[ 3 ]; }

Protokoll
(keine Ausgabe)

Abgesicherte Zugriffe

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array const a{ 4, 2, 7 };
::std::cout << a.at( 0 )<< '\n';
::std::cout << a.at( 1 )<< '\n';
::std::cout << a.at( 2 )<< '\n'; }

Protokoll

4

2

7

main.cpp

#include <array>
#include <initializer_list>

int main()
{ ::std::array const a{ 4, 2, 7 };
a.at( -1 ); }

Protokoll
This application has requested the Runtime to terminate it in an unusual way.
main.cpp

#include <array>
#include <initializer_list>

int main()
{ ::std::array const a{ 4, 2, 7 };
a.at( 3 ); }

Protokoll
This application has requested the Runtime to terminate it in an unusual way.

Die ungesicherten Zugriffe mit den eckigen Klammern »[« und »]« sind zu fehlerträchtig  und sollten daher nicht verwendet werden!

Statische Zugriffe

Hier muß der Wert des Typarguments schon bei der Übersetzung bekannt sein.

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::cout << ::std::get< 0 >( a )<< '\n'; }

Protokoll
4
main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::cout << ::std::get< -1 >( a )<< '\n'; }

Protokoll
compiler error: static assertion failed: array index is within bounds
main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::cout << ::std::get< 3 >( a )<< '\n'; }

Protokoll
compiler error: static assertion failed: array index is within bounds

Die Größe

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array const a{ 4, 2, 7 };
::std::cout << a.size() << '\n'; }

Protokoll
3

Es handelt sich um eine constexpr-Funktion!

»value_type«

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <string>

using namespace ::std::literals;

int main()
{ { ::std::array const a{ 4, 2, 7 };
decltype( a )::value_type const v{ ::std::get< 0 >( a ) };
::std::cout << v << '\n'; }

{ ::std::array const a{ "alpha"s, "gamma"s, "delta"s };
decltype( a )::value_type const v{ ::std::get< 0 >( a ) };
::std::cout << v << '\n'; }}

Protokoll

4

alpha

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <string>

using namespace ::std::literals;

int main()
{ { ::std::array const a{ 4, 2, 7 };
auto const v{ ::std::get< 0 >( a ) };
::std::cout << v << '\n'; }

{ ::std::array const a{ "alpha"s, "gamma"s, "delta"s };
auto const v{ ::std::get< 0 >( a ) };
::std::cout << v << '\n'; }}

Protokoll

4

alpha

»fill«

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <string>

using namespace ::std::literals;

int main()
{ ::std::array a{ 4, 2, 7 };
a.fill( 0 );
::std::cout << ::std::get< 0 >( a ) << ", "s
<< ::std::get< 1 >( a ) << ", "s
<< ::std::get< 2 >( a ) << '\n'; }

Protokoll
0, 0, 0

»swap«

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <string>

using namespace ::std::literals;

int main()
{ ::std::array a{ 4, 2, 7 };
::std::array b{ 8, 3, 9 };
::std::cout << ::std::get< 0 >( a ) << ", "s
<< ::std::get< 1 >( a ) << ", "s
<< ::std::get< 2 >( a ) << '\n';
::std::cout << ::std::get< 0 >( b ) << ", "s
<< ::std::get< 1 >( b ) << ", "s
<< ::std::get< 2 >( b ) << '\n';
a.swap( b );
::std::cout << ::std::get< 0 >( a ) << ", "s
<< ::std::get< 1 >( a ) << ", "s
<< ::std::get< 2 >( a ) << '\n';
::std::cout << ::std::get< 0 >( b ) << ", "s
<< ::std::get< 1 >( b ) << ", "s
<< ::std::get< 2 >( b ) << '\n'; }

Protokoll

4, 2, 7

8, 3, 9

8, 3, 9

4, 2, 7

main.cpp

#include <algorithm> // swap
#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <string>

using namespace ::std::literals;

int main()
{ ::std::array a{ 4, 2, 7 };
::std::array b{ 8, 3, 9 };
::std::cout << ::std::get< 0 >( a ) << ", "s
<< ::std::get< 1 >( a ) << ", "s
<< ::std::get< 2 >( a ) << '\n';
::std::cout << ::std::get< 0 >( b ) << ", "s
<< ::std::get< 1 >( b ) << ", "s
<< ::std::get< 2 >( b ) << '\n';
::std::swap( a, b );
::std::cout << ::std::get< 0 >( a ) << ", "s
<< ::std::get< 1 >( a ) << ", "s
<< ::std::get< 2 >( a ) << '\n';
::std::cout << ::std::get< 0 >( b ) << ", "s
<< ::std::get< 1 >( b ) << ", "s
<< ::std::get< 2 >( b ) << '\n'; }

Protokoll

4, 2, 7

8, 3, 9

8, 3, 9

4, 2, 7

»front«

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::cout << a.front() << '\n'; }

Protokoll
4

»back«

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::cout << a.back() << '\n'; }

Protokoll
7

»data«

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::cout << a.data()[ 0 ]<< '\n'; }

Protokoll
4

»==«

main.cpp

#include <array>
#include <initializer_list>
#include <ios> // ::std::boolalpha
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::array b{ 4, 2, 7 };
::std::array c{ 4, 2, 8 };
::std::cout << ::std::boolalpha
<<( a == b )<< '\n'
<<( a == c )<< '\n'; }

Protokoll

true

false

»<«

main.cpp

#include <array>
#include <initializer_list>
#include <ios> // ::std::boolalpha
#include <iostream>
#include <ostream>

int main()
{ ::std::array a{ 4, 2, 7 };
::std::array b{ 4, 2, 7 };
::std::array c{ 4, 2, 8 };
::std::cout << ::std::boolalpha
<<( a < b )<< '\n'
<<( a < c )<< '\n'
<<( c < a )<< '\n'; }

Protokoll

false

true

false

Auch die anderen Vergleichsoperatoren, wie »>=« sind verfügbar.

Übungsaufgaben

/   Zählen

Schreiben Sie ein Programm mit einer Zählschleife, die von 0 bis 2 (einschließlich) zählt und den Wert der Zählvariablen bei jedem Schleifendurchlauf ausgibt.

Hinweis: Zur Lösung dieser Aufgabe kann eine while- oder for-Schleife verwendet werden.

Erwartete Ausgabe
0
1
2

/   Ausgeben

Erweitern Sie das folgende Programm um eine Schleife, welche von von 0 bis 2 (einschließlich) zählt und den Wert der Zählvariablen sowie den Wert der Abfolge an der entsprechenden Stelle bei jedem Schleifendurchlauf ausgibt.

main.cpp

#include <array>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <string>

using namespace ::std::literals;

int main()
{ ::std::array const a { 4, 2, 7 }; }

Erwartete Ausgabe
0: 4
1: 2
2: 7

/   Ausgeben (1)

Passen Sie Ihre Lösung der vorigen Aufgabe folgendermaßen an:

Die Schleife soll die Abfolge auch dann noch vollständig ausgeben, wenn die Abfolge verändert wird, selbst wenn die Anzahl der Komponenten der Abfolge sich dabei verändert.

Dabei soll es nicht nötig sein, nach einer Änderung der Abfolge noch Anpassungen an der Ausgabeschleife vornehmen zu müssen.

Erwartete Ausgabe für die Initialisierung »::std::array const a{ 4, 2, 7, 5, 0 };«
0: 4
1: 2
2: 7
3: 5
4: 0

/   Summieren

Passen Sie Ihre Lösung der vorigen Aufgabe folgendermaßen an:

Die Schleife soll auch noch die Zwischensumme der bisherigen Werte der Komponenten ausgeben.

Erwartete Ausgabe für die Initialisierung »::std::array const a{ 4, 2, 7, 5, 0 };«
0: 4 4
1: 2 6
2: 7 13
3: 5 18
4: 0 18

/   Rückwärtsausgabe

Passen Sie Ihre Lösung der vorigen Aufgabe folgendermaßen an:

Schreiben Sie die Ausgabeschleife so, daß sie eine int-Abfolge »a« beliebiger Größe rückwärts ausgibt.

Erwartete Ausgabe für die Initialisierung »::std::array const a{ 4, 2, 7, 5, 0 };«
4: 0
3: 5
2: 7
1: 2
0: 4

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram724204 stefan_ram:724204 Benutzerdefinierte Reihungen in C++ Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd724204, slrprddef724204, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/array_template_c++