(Remarque: cette question concerne le fait de ne pas avoir à spécifier le nombre d'éléments et de toujours permettre aux types imbriqués d'être directement initialisés.)
Cette question traite des utilisations restantes pour un tableau C comme int arr[20];
. Sur sa réponse , @James Kanze montre l'un des derniers bastions des tableaux C, ses caractéristiques d'initialisation uniques:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
Nous n'avons pas à spécifier le nombre d'éléments, hourra! Maintenant, parcourez-le avec les fonctions C ++ 11 std::begin
et std::end
from <iterator>
( ou vos propres variantes ) et vous n'avez même jamais besoin de penser à sa taille.
Maintenant, y a-t-il des moyens (éventuellement TMP) pour obtenir la même chose avec std::array
? L'utilisation de macros a permis de le rendre plus joli. :)
??? std_array = { "here", "be", "elements" };
Edit : La version intermédiaire, compilée à partir de diverses réponses, ressemble à ceci:
#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);
Et utilise toutes sortes de trucs sympas C ++ 11:
- Modèles Variadic
sizeof...
- références rvalue
- acheminement parfait
std::array
, bien sûr- initialisation uniforme
- omettre le type de retour avec une initialisation uniforme
- inférence de type (
auto
)
Et un exemple peut être trouvé ici .
Cependant , comme le souligne @Johannes dans le commentaire sur la réponse de @ Xaade, vous ne pouvez pas initialiser des types imbriqués avec une telle fonction. Exemple:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
En outre, le nombre d'initialiseurs est limité au nombre d'arguments de fonction et de modèle pris en charge par l'implémentation.
TMP
votre question?Réponses:
Le mieux que je puisse penser est:
Cependant, cela nécessite que le compilateur fasse NRVO, puis ignore également la copie de la valeur retournée (ce qui est également légal mais non obligatoire). En pratique, je m'attendrais à ce que n'importe quel compilateur C ++ puisse l'optimiser de telle sorte qu'il soit aussi rapide que l'initialisation directe.
la source
make_array
appel.static_assert
et du TMP pour détecter quand ilTail
n'est pas implicitement convertible enT
, puis en utilisantT(tail)...
, mais cela reste comme exercice pour le lecteur :)Je m'attendrais à un simple
make_array
.la source
std::array<ret, sizeof...(T)>
sur lareturn
déclaration. Cela force inutilement un constructeur de déplacement sur le type de tableau à exister (par opposition à une construction à partir deT&&
) en C ++ 14 et C ++ 11.En combinant quelques idées des articles précédents, voici une solution qui fonctionne même pour les constructions imbriquées (testée dans GCC4.6):
Étrangement, ne peut pas faire de la valeur de retour une référence rvalue, qui ne fonctionnerait pas pour les constructions imbriquées. Bref, voici un test:
(Pour la dernière sortie, j'utilise ma jolie imprimante .)
En fait, améliorons la sécurité de type de cette construction. Nous avons certainement besoin que tous les types soient identiques. Une façon est d'ajouter une assertion statique, que j'ai modifiée ci-dessus. L'autre façon est de n'activer que
make_array
lorsque les types sont identiques, comme ceci:Dans tous les cas, vous aurez besoin du
all_same<Args...>
trait de type variadique . Ici , il est, de généralisaitstd::is_same<S, T>
(notez que putréfaction est important pour permettre le mélange deT
,T&
,T const &
etc.):Notez que les
make_array()
retours par copie de temporaire, que le compilateur (avec suffisamment d'indicateurs d'optimisation!) Est autorisé à traiter comme une rvalue ou à optimiser autrement, etstd::array
est un type d'agrégat, le compilateur est donc libre de choisir la meilleure méthode de construction possible .Enfin, notez que vous ne pouvez pas éviter la construction copier / déplacer lors de la
make_array
configuration de l'initialiseur. Donc,std::array<Foo,2> x{Foo(1), Foo(2)};
il n'y a pas de copie / déplacement, maisauto x = make_array(Foo(1), Foo(2));
a deux copies / déplacements lorsque les arguments sont transférésmake_array
. Je ne pense pas que vous puissiez améliorer cela, car vous ne pouvez pas passer une liste d'initialiseurs variadiques lexicalement à l'assistant et en déduire le type et la taille - si le préprocesseur avait unesizeof...
fonction pour les arguments variadiques, peut-être que cela pourrait être fait, mais pas dans la langue de base.la source
L'utilisation de la syntaxe de retour de fin
make_array
peut être encore simplifiéeMalheureusement pour les classes agrégées, il nécessite une spécification de type explicite
En fait, cette
make_array
implémentation est répertoriée dans sizeof ... opérateurversion c ++ 17
Grâce à la déduction des arguments de modèle pour la proposition de modèles de classe, nous pouvons utiliser des guides de déduction pour se débarrasser de l'
make_array
assistantCompilé avec le
-std=c++1z
drapeau sous x86-64 gcc 7.0la source
C ++ 11 prendra en charge cette manière d'initialiser pour (la plupart?) Les conteneurs std.
la source
std::vector<>
n'a pas besoin de l'entier explicite, et je ne sais pas pourquoistd::array
.{...}
syntaxe implique une étendue constante au moment de la compilation, donc le contrôleur devrait être capable de déduire l'étendue.std::initializer_list::size
n'est pas uneconstexpr
fonction et ne peut donc pas être utilisée de cette manière. Il y a cependant des plans de libstdc ++ (l'implémentation livrée avec GCC) pour avoir leur versionconstexpr
.Je sais que cela fait un certain temps que cette question a été posée, mais je pense que les réponses existantes ont encore des lacunes, alors j'aimerais proposer ma version légèrement modifiée. Voici les points pour lesquels je pense que certaines réponses existantes manquent.
1. Pas besoin de compter sur RVO
Certaines réponses mentionnent que nous devons nous fier à RVO pour renvoyer le construit
array
. Ce n'est pas vrai; nous pouvons utiliser l' initialisation de la liste de copies pour garantir qu'il n'y aura jamais de création de temporaires. Donc au lieu de:nous devrions faire:
2. Créer
make_array
uneconstexpr
fonctionCela nous permet de créer des tableaux de constantes au moment de la compilation.
3. Pas besoin de vérifier que tous les arguments sont du même type
Tout d'abord, s'ils ne le sont pas, le compilateur émettra un avertissement ou une erreur de toute façon car l'initialisation de la liste ne permet pas de rétrécir. Deuxièmement, même si nous décidons vraiment de faire notre propre
static_assert
truc (peut-être pour fournir un meilleur message d'erreur), nous devrions probablement comparer les types décomposés des arguments plutôt que les types bruts. Par exemple,Si nous faisons simplement
static_assert
celaa
,b
et que nousc
avons le même type, alors cette vérification échouera, mais ce n'est probablement pas ce à quoi nous nous attendions. Au lieu de cela, nous devrions comparer leursstd::decay_t<T>
types (qui sont tous desint
s)).4. Déduisez le type de valeur du tableau en décomposant les arguments transmis
Ceci est similaire au point 3. En utilisant le même extrait de code, mais ne spécifiez pas le type de valeur explicitement cette fois:
Nous voulons probablement faire un
array<int, 3>
, mais les implémentations dans les réponses existantes échouent probablement toutes à le faire. Ce que nous pouvons faire est, au lieu de renvoyer unstd::array<T, …>
, renvoyer unstd::array<std::decay_t<T>, …>
.Il y a un inconvénient à cette approche: nous ne pouvons plus renvoyer un
array
type de valeur qualifié cv. Mais la plupart du temps, au lieu de quelque chose comme unarray<const int, …>
, nous utiliserions unconst array<int, …>
quand même . Il y a un compromis, mais je pense qu'il est raisonnable. Le C ++ 17std::make_optional
adopte également cette approche:En tenant compte des points ci-dessus, une implémentation fonctionnelle complète de
make_array
C ++ 14 ressemble à ceci:Usage:
la source
(Solution par @dyp)
Remarque: nécessite C ++ 14 (
std::index_sequence
). Bien que l'on puisse implémenterstd::index_sequence
en C ++ 11.la source
make_array
comme dans la réponse de Puppy, cependant.Implémentation compacte С ++ 17.
la source
Si std :: array n'est pas une contrainte et si vous avez Boost, jetez un œil à
list_of()
. Ce n'est pas exactement comme l'initialisation de tableau de type C que vous souhaitez. Mais proche.la source
Créez un type de fabricant de baie.
Il surcharge
operator,
pour générer un modèle d'expression chaînant chaque élément au précédent via des références.Ajouter un
finish
fonction gratuite qui prend le générateur de tableaux et génère un tableau directement à partir de la chaîne de références.La syntaxe devrait ressembler à ceci:
Il ne permet pas la
{}
construction basée, comme le fait seulementoperator=
. Si vous êtes prêt à utiliser,=
nous pouvons le faire fonctionner:ou
Aucune de ces solutions ne ressemble à de bonnes solutions.
L'utilisation de variardics vous limite à la limite imposée par le compilateur au nombre de varargs et bloque l'utilisation récursive de
{}
pour les sous-structures.En fin de compte, il n'y a vraiment pas de bonne solution.
Ce que je fais, c'est d'écrire mon code pour qu'il consomme à la fois
T[]
et lesstd::array
données de manière agnostique - peu importe que je le nourris. Parfois, cela signifie que mon code de transfert doit soigneusement transformer les[]
tableaux enstd::array
s de manière transparente.la source