Je me familiarise avec C++ 0x et je teste des choses avec g ++ 4.6
J'ai juste essayé le code suivant, pensant que cela fonctionnerait, mais il ne compile pas. Je reçois l'erreur:
incompatible types in assignment of ‘std::initializer_list<const int>’ to ‘const int [2]’
struct Foo
{
int const data[2];
Foo(std::initializer_list<int const>& ini)
: data(ini)
{}
};
Foo f = {1,3};
Vous pouvez utiliser un constructeur de modèle variadique au lieu d'un constructeur de liste d'initialiseur:
struct foo {
int x[2];
template <typename... T>
foo(T... ts) : x{ts...} { // note the use of brace-init-list
}
};
int main() {
foo f1(1,2); // OK
foo f2{1,2}; // Also OK
foo f3(42); // OK; x[1] zero-initialized
foo f4(1,2,3); // Error: too many initializers
foo f5(3.14); // Error: narrowing conversion not allowed
foo f6("foo"); // Error: no conversion from const char* to int
}
EDIT: Si vous pouvez vivre sans constance, une autre façon serait de sauter l’initialisation et de remplir le tableau dans le corps de la fonction
struct foo {
int x[2]; // or std::array<int, 2> x;
foo(std::initializer_list<int> il) {
std::copy(il.begin(), il.end(), x);
// or std::copy(il.begin(), il.end(), x.begin());
// or x.fill(il.begin());
}
}
De cette façon, vous perdez les limites de compilation en vérifiant ce que l'ancienne solution fournit.
Autant que je sache, utiliser l'initialisation de liste de l'argument de fonction du constructeur (8.5.4/1) devrait être légal et résoudre bon nombre des problèmes ci-dessus. Cependant, GCC 4.5.1 sur ideone.com ne correspond pas au constructeur et le rejette.
#include <array>
struct Foo
{
std::array< int, 2 > const data;
Foo(std::array<int, 2> const& ini) // parameter type specifies size = 2
: data( ini )
{}
};
Foo f( {1,3} ); // list-initialize function argument per 8.5.4/1
Si vous insistez vraiment sur initializer_list
, vous pouvez utiliser reinterpret_cast
pour transformer le tableau sous-jacent du initializer_list
en un tableau de style C.
Foo(std::initializer_list<int> ini) // pass without reference- or cv-qualification
: data( reinterpret_cast< std::array< int, 2 > const & >( * ini.begin() )
Selon la discussion ici :
la syntaxe correcte pour la deuxième solution de Potatoswatter est la suivante:
Foo f( {{1,3}} ); //two braces
un peu moche, pas conforme à l'usage courant
Juste un petit ajout à la grande JohannesD répondre .
Si aucun argument n'est passé au constructeur foo
, array sera initialisé par défaut. Mais parfois, vous voulez garder le tableau sous-jacent non initié (peut-être pour des raisons de performances). Vous ne pouvez pas ajouter de constructeur par défaut avec un modèle basé sur variadic. La solution de contournement est un argument supplémentaire pour le constructeur à modèles variadiques, permettant de le distinguer du constructeur à argument zéro:
template<class T, size_t rows, size_t cols>
class array2d
{
std::array<T, rows * cols> m_Data;
public:
array2d() {}
template <typename T, typename... Types>
array2d(T t, Types... ts) : m_Data{ { t, ts... } } {}
};
Donc, maintenant vous pouvez initier un objet, ou le laisser non initialisé:
array2d<int, 6, 8> arr = { 0, 1, 2, 3 }; // contains 0, 1, 2, 3, 0, 0, 0, ...
array2d<int, 6, 8> arr2; // contains garbage
Mise à jour 31/07/2016
Trois années se sont écoulées rapidement et les développeurs de compilateurs ont amélioré la conformité standard de leurs produits jusqu'au niveau où le constructeur par défaut n'est plus considéré comme ambigu en présence de constructeur variadique. Donc, en pratique, nous n’avons pas besoin d’un argument supplémentaire T t
avec un constructeur variadique pour lever l’ambiguïté des constructeurs.
Tous les deux
array2d() {}
et
array2d() = default;
laissera un tableau non initialisé si l'objet est construit sans arguments. Ce comportement est cohérent sur tous les principaux compilateurs. Exemple complet ( rextester ):
#include <array>
#include <iostream>
template<class T, size_t rows, size_t cols>
class array2d
{
public:
std::array<T, rows * cols> m_Data;
array2d() = default;
template <typename... Types>
array2d(Types... ts) : m_Data{ { ts... } } {}
};
int main()
{
array2d<int, 6, 8> arr_init = { 0, 1, 2, 3 };
array2d<int, 6, 8> arr_default;
std::cout << "Initialized: \n";
for(const auto& a : arr_init.m_Data)
std::cout << a << " ";
std::cout << "\n";
std::cout << "Default: \n";
for(const auto& a : arr_default.m_Data)
std::cout << a << " ";
std::cout << "\n";
}
Sortie:
Initialized:
0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Default:
2 0 -519559849 32558 1 32558 0 0 -519634912 32558 -526739248 32558 1 0 2 0 6295032 0 -519531243 32558 0 0 -1716075168 32765 6295648 0 4196192 0 6295648 0 -526527271 32558 1 0 2 0 6295032 0 4196845 0 124 0 0 0 4196768 0 4196518 0
La suppression du constructeur par défaut conduit toujours à appeler le constructeur variadique et à initialiser le tableau par défaut (dans ce cas, nous utilisons uniquement des zéros).
Merci à @Alek d'avoir bousillé ce fil et d'avoir attiré l'attention sur ces faits, et merci également à toutes les personnes qui travaillent dur au développement du compilateur.
Vous ne pouvez pas, les tableaux ne sont pas comme les autres types (et les constructeurs ne prennent pas std :: initializer_list).
Essayez ceci à la place:
struct Foo
{
const std::vector<int> data;
Foo(std::initializer_list<int> ini) : data(ini)
{}
};
Bien que cela ne fonctionne pas:
#include <initializer_list>
struct Foo
{
const int data[2];
constexpr Foo(const std::initializer_list<int>& ini): data{ini} {}
};
Foo f = {1,3};
J'ai trouvé cette approche simple pour bien fonctionner:
struct Foo
{
const int data[2];
constexpr Foo(const int a, const int b): data{a,b} {}
};
Foo f = {1,3};
Bien sûr, l’approche par modèles variadiques est probablement préférable si vous avez beaucoup d’éléments, mais dans ce cas simple, cela suffira probablement.
En d’autres termes, si vous souhaitez définir explicitement le constructeur à partir de listes d’initialisation. Pour la plupart des cas de POD, tout va bien:
struct Foo
{
const int data[2];
};
Foo f = {1,3};
Si vous ne vous souciez pas de la vérification des limites, alors ce qui suit fonctionnera.
struct Foo {
int const data[2];
Foo(std::initializer_list<int> ini)
: data{*std::begin(ini), *std::next(std::begin(ini), 1)} {}
};