ostream, opérateur << : problème avec les namespaces ?
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 :
Code:
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)
{
// ...
}
} |
À 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:
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;
}
} |
Le compilateur semble ne pas trouver l'opérateur en question :
Citation:
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>]’
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 :
Code:
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 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).
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).