![So emulieren Sie die C-Array-Initialisierung "int arr[] = { e1, e2, e3, ... }" Verhalten mit std::array? 1 So emulieren Sie die C Array Initialisierung int arr e1 e2](https://www.viresist.org/wp-content/uploads/So-emulieren-Sie-die-C-Array-Initialisierung-int-arr-e1-e2.png)
Xeo
(Hinweis: Bei dieser Frage geht es darum, die Anzahl der Elemente nicht angeben zu müssen und dennoch die direkte Initialisierung verschachtelter Typen zuzulassen.)
Diese Frage diskutiert die verbleibenden Verwendungen für ein C-Array wie int arr[20];
. In seiner Antwort zeigt @James Kanze eine der letzten Hochburgen von C-Arrays, ihre einzigartigen Initialisierungsmerkmale:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
Wir müssen die Anzahl der Elemente nicht angeben, hurra! Iterieren Sie nun mit den C++11-Funktionen darüber std::begin
und std::end
von <iterator>
(oder Ihre eigenen Varianten) und Sie müssen nicht einmal an seine Größe denken.
Gibt es nun (möglicherweise TMP) Möglichkeiten, dasselbe zu erreichen? std::array
? Die Verwendung von Makros ist erlaubt, um es schöner aussehen zu lassen. 🙂
??? std_array = { "here", "be", "elements" };
Bearbeiten: Zwischenversion, zusammengestellt aus verschiedenen Antworten, sieht so aus:
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
Und verwendet alle möglichen coolen C++11-Sachen:
- Variadische Vorlagen
sizeof...
- rvalue-Referenzen
- perfekte Weiterleitung
std::array
selbstverständlich
- einheitliche Initialisierung
- Weglassen des Rückgabetyps bei einheitlicher Initialisierung
- Typschluss (
auto
)
Und ein Beispiel lässt sich finden Hier.
aber, wie @Johannes im Kommentar zur Antwort von @Xaade betont, können Sie mit einer solchen Funktion keine verschachtelten Typen initialisieren. Beispiel:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
Außerdem ist die Anzahl der Initialisierer auf die Anzahl der von der Implementierung unterstützten Funktions- und Vorlagenargumente beschränkt.
![So emulieren Sie die C-Array-Initialisierung "int arr[] = { e1, e2, e3, ... }" Verhalten mit std::array? 2 So emulieren Sie die C Array Initialisierung int arr e1 e2](https://www.viresist.org/wp-content/uploads/So-emulieren-Sie-die-C-Array-Initialisierung-int-arr-e1-e2.jpg)
Pavel Minaev
Das Beste was mir einfällt ist:
template<class T, class... Tail>
auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
{
std::array<T, 1 + sizeof...(Tail)> a = { head, tail ... };
return a;
}
auto a = make_array(1, 2, 3);
Dies erfordert jedoch, dass der Compiler NRVO ausführt und dann auch die Kopie des zurückgegebenen Werts überspringt (was ebenfalls zulässig, aber nicht erforderlich ist). In der Praxis würde ich erwarten, dass jeder C++-Compiler dies so optimieren kann, dass es so schnell ist wie eine direkte Initialisierung.
![So emulieren Sie die C-Array-Initialisierung "int arr[] = { e1, e2, e3, ... }" Verhalten mit std::array? 3 So emulieren Sie die C Array Initialisierung int arr e1 e2](https://www.viresist.org/wp-content/uploads/So-emulieren-Sie-die-C-Array-Initialisierung-int-arr-e1-e2.jpeg)
Hündchen
Ich würde ein einfaches erwarten make_array
.
template<typename ret, typename... T> std::array<ret, sizeof...(T)> make_array(T&&... refs) {
// return std::array<ret, sizeof...(T)>{ { std::forward<T>(refs)... } };
return { std::forward<T>(refs)... };
}
![So emulieren Sie die C-Array-Initialisierung "int arr[] = { e1, e2, e3, ... }" Verhalten mit std::array? 4 1646926815 984 So emulieren Sie die C Array Initialisierung int arr e1 e2](https://www.viresist.org/wp-content/uploads/1646926815_984_So-emulieren-Sie-die-C-Array-Initialisierung-int-arr-e1-e2.png)
Kerrek SB
Hier ist eine Lösung, die einige Ideen aus früheren Beiträgen kombiniert und sogar für verschachtelte Konstruktionen funktioniert (getestet in GCC4.6):
template <typename T, typename ...Args>
std::array<T, sizeof...(Args) + 1> make_array(T && t, Args &&... args)
{
static_assert(all_same<T, Args...>::value, "make_array() requires all arguments to be of the same type."); // edited in
return std::array<T, sizeof...(Args) + 1>{ std::forward<T>
}
Seltsamerweise kann der Rückgabewert nicht zu einer Rvalue-Referenz gemacht werden, die für verschachtelte Konstruktionen nicht funktionieren würde. Wie auch immer, hier ist ein Test:
auto q = make_array(make_array(make_array(std::string("Cat1"), std::string("Dog1")), make_array(std::string("Mouse1"), std::string("Rat1"))),
make_array(make_array(std::string("Cat2"), std::string("Dog2")), make_array(std::string("Mouse2"), std::string("Rat2"))),
make_array(make_array(std::string("Cat3"), std::string("Dog3")), make_array(std::string("Mouse3"), std::string("Rat3"))),
make_array(make_array(std::string("Cat4"), std::string("Dog4")), make_array(std::string("Mouse4"), std::string("Rat4")))
);
std::cout << q << std::endl;
// produces: [[[Cat1, Dog1], [Mouse1, Rat1]], [[Cat2, Dog2], [Mouse2, Rat2]], [[Cat3, Dog3], [Mouse3, Rat3]], [[Cat4, Dog4], [Mouse4, Rat4]]]
(Für die letzte Ausgabe verwende ich meinen Pretty-Printer.)
Lassen Sie uns eigentlich die Typensicherheit dieser Konstruktion verbessern. Wir brauchen definitiv alle Typen, um gleich zu sein. Eine Möglichkeit besteht darin, eine statische Behauptung hinzuzufügen, die ich oben bearbeitet habe. Die andere Möglichkeit besteht darin, nur zu aktivieren make_array
Wenn die Typen gleich sind, so:
template <typename T, typename ...Args>
typename std::enable_if<all_same<T, Args...>::value, std::array<T, sizeof...(Args) + 1>>::type
make_array(T && t, Args &&... args)
{
return std::array<T, sizeof...(Args) + 1> { std::forward<T>
}
In jedem Fall benötigen Sie das Variadic all_same<Args...>
Typ Eigenschaft. Hier ist es, verallgemeinernd aus std::is_same<S, T>
(Beachten Sie, dass das Zerfallen wichtig ist, um das Mischen zu ermöglichen T
, T&
, T const &
etc.):
template <typename ...Args> struct all_same { static const bool value = false; };
template <typename S, typename T, typename ...Args> struct all_same<S, T, Args...>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value && all_same<T, Args...>::value;
};
template <typename S, typename T> struct all_same<S, T>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value;
};
template <typename T> struct all_same<T> { static const bool value = true; };
Beachten Sie, dass make_array()
gibt von copy-of-temporary zurück, die der Compiler (mit genügend Optimierungs-Flags!) als rvalue behandeln oder anderweitig wegoptimieren darf, und std::array
ist ein Aggregattyp, sodass es dem Compiler freisteht, die bestmögliche Konstruktionsmethode auszuwählen.
Beachten Sie schließlich, dass Sie die Erstellung von Kopieren/Verschieben nicht vermeiden können, wenn make_array
richtet den Initialisierer ein. Damit std::array<Foo,2> x{Foo(1), Foo(2)};
hat kein kopieren/verschieben, aber auto x = make_array(Foo(1), Foo(2));
hat zwei Kopien/Verschiebungen, an die die Argumente weitergeleitet werden make_array
. Ich glaube nicht, dass Sie das verbessern können, da Sie dem Helfer keine variadische Initialisierungsliste lexikalisch übergeben können und Typ und Größe ableiten – wenn der Präprozessor a hatte sizeof...
Funktion für variadische Argumente, vielleicht könnte das getan werden, aber nicht innerhalb der Kernsprache.
![So emulieren Sie die C-Array-Initialisierung "int arr[] = { e1, e2, e3, ... }" Verhalten mit std::array? 5 1646926816 315 So emulieren Sie die C Array Initialisierung int arr e1 e2](https://www.viresist.org/wp-content/uploads/1646926816_315_So-emulieren-Sie-die-C-Array-Initialisierung-int-arr-e1-e2.png)
abgewischt
Verwendung der Trailing-Return-Syntax make_array
weiter vereinfacht werden kann
#include <array>
#include <type_traits>
#include <utility>
template <typename... T>
auto make_array(T&&... t)
-> std::array<std::common_type_t<T...>, sizeof...
{
return {std::forward<T>
}
int main()
{
auto arr = make_array(1, 2, 3, 4, 5);
return 0;
}
Leider erfordert es für Aggregatklassen eine explizite Typangabe
/*
struct Foo
{
int a, b;
}; */
auto arr = make_array(Foo{1, 2}, Foo{3, 4}, Foo{5, 6});
In der Tat dies make_array
Implementierung ist in aufgeführt sizeof…-Operator
c++17-Version
Dank an Template-Argumentableitung für Klassen-Templates Vorschlag, den wir mithilfe von Abzugshilfen loswerden können make_array
Helfer
#include <array>
namespace std
{
template <typename... T> array(T... t)
-> array<std::common_type_t<T...>, sizeof...
}
int main()
{
std::array a{1, 2, 3, 4};
return 0;
}
Kompiliert mit -std=c++1z
Flagge unter x86-64 gcc 7.0
![So emulieren Sie die C-Array-Initialisierung "int arr[] = { e1, e2, e3, ... }" Verhalten mit std::array? 6 1646926816 801 So emulieren Sie die C Array Initialisierung int arr e1 e2](https://www.viresist.org/wp-content/uploads/1646926816_801_So-emulieren-Sie-die-C-Array-Initialisierung-int-arr-e1-e2.png)
Zizheng Tai
Ich weiß, es ist schon einige Zeit her, dass diese Frage gestellt wurde, aber ich habe das Gefühl, dass die vorhandenen Antworten immer noch einige Mängel aufweisen, daher möchte ich meine leicht modifizierte Version vorschlagen. Im Folgenden sind die Punkte aufgeführt, von denen ich denke, dass einige vorhandene Antworten fehlen.
1. Sie müssen sich nicht auf RVO verlassen
Einige Antworten erwähnen, dass wir uns auf RVO verlassen müssen, um das Konstruierte zurückzugeben array
. Das ist nicht wahr; können wir nutzen Copy-List-Initialisierung um sicherzustellen, dass niemals Provisorien erstellt werden. Also statt:
return std::array<Type, …>{values};
wir sollten tun:
return {{values}};
2. Machen make_array
ein constexpr
Funktion
Dies ermöglicht es uns, konstante Arrays zur Kompilierzeit zu erstellen.
3. Es muss nicht überprüft werden, ob alle Argumente vom gleichen Typ sind
Erstens, wenn dies nicht der Fall ist, gibt der Compiler trotzdem eine Warnung oder einen Fehler aus, da die Listeninitialisierung keine Einschränkung zulässt. Zweitens, auch wenn wir uns wirklich entscheiden, es selbst zu tun static_assert
Sache (vielleicht um eine bessere Fehlermeldung zu liefern), sollten wir wahrscheinlich trotzdem die Argumente vergleichen’ verfallen Typen statt Rohtypen. Zum Beispiel,
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array<int>(a, b, c); // Will this work?
Wenn wir einfach sind static_assert
ing das a
, b
und c
denselben Typ haben, schlägt diese Prüfung fehl, aber das ist wahrscheinlich nicht das, was wir erwarten würden. Stattdessen sollten wir ihre vergleichen std::decay_t<T>
Typen (das sind alle int
S)).
4. Leiten Sie den Array-Werttyp ab, indem Sie die weitergeleiteten Argumente zerfallen lassen
Dies ähnelt Punkt 3. Verwenden Sie dasselbe Code-Snippet, aber geben Sie diesmal den Werttyp nicht explizit an:
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array(a, b, c); // Will this work?
Wir wollen wahrscheinlich eine machen array<int, 3>
, aber die Implementierungen in den vorhandenen Antworten tun dies wahrscheinlich nicht. Was wir tun können, ist, anstatt a zurückzugeben std::array<T, …>
gib a zurück std::array<std::decay_t<T>, …>
.
Dieser Ansatz hat einen Nachteil: Wir können kein zurückgeben array
des Lebenslauf-qualifizierten Werttyps nicht mehr. Aber meistens statt so etwas wie ein array<const int, …>
würden wir a verwenden const array<int, …>
ohnehin. Es gibt einen Kompromiss, aber ich denke, ein vernünftiger. Das C++17 std::make_optional
geht auch so vor:
template< class T >
constexpr std::optional<std::decay_t<T>> make_optional( T&& value );
Unter Berücksichtigung der oben genannten Punkte ist eine voll funktionsfähige Implementierung von make_array
in C++14 sieht so aus:
#include <array>
#include <type_traits>
#include <utility>
template<typename T, typename... Ts>
constexpr std::array<std::decay_t<T>, 1 + sizeof... (Ts)>
make_array(T&& t, Ts&&... ts)
noexcept(noexcept(std::is_nothrow_constructible<
std::array<std::decay_t<T>, 1 + sizeof... (Ts)>, T&&, Ts&&...
>::value))
{
return {{std::forward<T>
}
template<typename T>
constexpr std::array<std::decay_t<T>, 0> make_array() noexcept
{
return {};
}
Verwendungszweck:
constexpr auto arr = make_array(make_array(1, 2),
make_array(3, 4));
static_assert(arr[1][1] == 4, "!");
![So emulieren Sie die C-Array-Initialisierung "int arr[] = { e1, e2, e3, ... }" Verhalten mit std::array? 7 1646926817 51 So emulieren Sie die C Array Initialisierung int arr e1 e2](https://www.viresist.org/wp-content/uploads/1646926817_51_So-emulieren-Sie-die-C-Array-Initialisierung-int-arr-e1-e2.png)
unterstrich_d
C++11 wird unterstützt diese Art der Initialisierung für (die meisten?) Std-Container.
(Lösung von @dyp)
Hinweis: erfordert C++14 (std::index_sequence
). Obwohl man das umsetzen könnte std::index_sequence
in C++11.
#include <iostream>
// ---
#include <array>
#include <utility>
template <typename T>
using c_array = T[];
template<typename T, size_t N, size_t... Indices>
constexpr auto make_array(T (&&src)[N], std::index_sequence<Indices...>) {
return std::array<T, N>{{ std::move(src[Indices])... }};
}
template<typename T, size_t N>
constexpr auto make_array(T (&&src)[N]) {
return make_array(std::move(src), std::make_index_sequence<N>{});
}
// ---
struct Point { int x, y; };
std::ostream& operator<< (std::ostream& os, const Point& p) {
return os << "(" << p.x << "," << p.y << ")";
}
int main() {
auto xs = make_array(c_array<Point>{{1,2}, {3,4}, {5,6}, {7,8}});
for (auto&& x : xs) {
std::cout << x << std::endl;
}
return 0;
}
9883400cookie-checkSo emulieren Sie die C-Array-Initialisierung “int arr[] = { e1, e2, e3, … }” Verhalten mit std::array?yes
Variadische Methode. Es ist keine Initialisierung, eher eine Zuweisung, aber es kommt mir am nächsten. Um die Initialisierung zu erhalten, müssten Sie direkten Zugriff auf den Speicher haben.
– Lee Louviere
24. Mai 2011 um 17:03 Uhr
Anscheinend unterstützt C++0x die Initialisierungssyntax. Fantastisch. Es ist, als würde man C# ähnlicher werden, mit Sprachunterstützung für kompliziertere Unterstützung. Weiß jemand, ob wir formale Sprachunterstützung für Schnittstellen bekommen???
– Lee Louviere
24. Mai 2011 um 17:06 Uhr
@Downvoter: Grund?
– Xeo
2. Juni 2011 um 20:13 Uhr
Entschuldigung, was ist die Bedeutung von
TMP
in deiner frage?– Kevinarpe
23. August 2016 um 14:36 Uhr
@kevinarpe TMP steht wahrscheinlich für Template-Metaprogrammierung.
– BeeOnRope
28. Mai 2017 um 20:23 Uhr