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 : 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)
    {
        // ...
    }
}
À 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 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 :
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 : 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 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).