Bonjour,
Je viens de tomber sur un problème qui me dépasse...
Jusqu'à présent, j'ai eu souvent l'occasion d'introduire de nouveaux opérateurs de flux vers std::ostream, et ça ne m'a jamais posé de soucis. Mais j'ai eu besoin aujourd'hui, pour déboguer un programme, de sérialiser un std::array<float,4>.
Pour m'éviter la corvée de tout ré-écrire à chaque fois, je me suis dit que ça ne pourrait pas faire de mal d'écrire un opérateur << template pour gérer tous les cas possibles :
À ma grande surprise, je n'ai pas été capable de l'utiliser au sein de mon namespace gui (autre que celui dans lequel j'ai défini l'opérateur : utils) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 namespace utils { template<class T, size_t N> std::ostream& operator << (std::ostream& o, const std::array<T, N>& a) { // ... } }
Le compilateur semble ne pas trouver l'opérateur en question :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 namespace gui { void foo() { std::array<float,4> a = {{1,2,3,4}}; std::cout << a << std::endl; } }
Cela semble provenir d'un conflit avec un autre opérateur << que j'ai écrit au sein du namespace gui pour un type perso. En effet, voici un exemple minimal qui reproduit le problème :test.cpp: In function ‘void gui::foo()’:
test.cpp:32:22: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
/usr/include/c++/4.6/ostream:581:5: error: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::array<float, 4u>]’
Si on commente le type bidon color et son opérateur << associé, alors ça compile. Sinon, le compilateur ne trouve pas l'opérateur pour le std::array (il me sort l'erreur citée plus haut, qui apparaît également si on commente l'opérateur << pour std::array).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 #include <iostream> #include <array> namespace utils { // L'operateur que je souhaite rajouter template<class T, size_t N> std::ostream& operator << (std::ostream& o, const std::array<T, N>& a) { o << "("; for (size_t i = 0; i < N; ++i) { if (i != N-1) o << a[i] << ", "; else o << a[i]; } o << ")"; return o; } } // Un namespace quelconque namespace gui { // Un nouveau type bidon, juste pour introduire un autre operateur << struct color { unsigned int pack; }; // ... que voici std::ostream& operator << (std::ostream& o, const color& c) { return o << c.pack; } // La fonction qui pose problème void foo() { std::array<float, 4> a = {{1, 2, 3, 4}}; std::cout << a << std::endl; } } int main(int argc, char* argv[]) { gui::foo(); return 0; }
Si on met l'opérateur << pour std::array dans le namespace global, ça ne change rien.
Les seules manières que j'ai trouvées pour que le code compile sont donc :
- ne définir aucun autre opérateur << dans le namespace gui (vraiment louche),
- placer l'opérateur << pour std::array dans le namespace gui (pénible, il faut le faire pour chaque namespace),
- appeler explicitement l'opérateur avec son namespace : utils::operator << (std::cout, a) (lourdingue),
- ou enfin le définir directement dans le namespace std (ce qui permet d'utiliser le Koenig lookup, si je ne m'abuse, mais je n'aime pas trop magouiller avec le namespace std).
Avez-vous une idée de ce qui cloche ?
En y réfléchissant, je pense qu'il est normal que le code ne compile pas, puisqu'aucun des arguments de l'opérateur << n'appartient au namespace utils, et donc le Koenig lookup ne peut pas fonctionner. Mais alors pourquoi est-ce que ça compile s'il n'y a aucun autre opérateur de flux dans le namespace gui ?
Je suis sous linux, et compile avec gcc 4.6.1 (avec l'option -std=c++0x, bien entendu).
Partager