01 sept. 2008

Etude du C++ Technical Report 1 - reference_wrapper : je confirme, c'est tordu.

Souvenez vous : il y a près de deux ans, j'essayais avec grand peine d'implémenter la classe std::tr1::reference_wrapper<>, et j'ai du abandonner.Tout content de pouvoir installer l'implémentation du TR1 de Microsoft Visual C++ 2008 SP1, j'ai tenu à vérifier que mon échec épique n'était pas du à ma stupidité, mais à (comme je le pense sincèrement) une mauvaise formulation du texte du TR1.

Le problème se situe dans le document ISO/IEC DT 19768 (pdf), section 2.1.2, paragraphes 2, 3, 4.

2. reference_wrapper has a weak result type (3.3).

3. The template instantiation reference_wrapper<T> shall be derived from std::unary_function<T1, R> only if the type T is any of the following:
-- a function type or a pointer to function type taking one argument of type T1 and returning R
-- a pointer to member function type with cv-qualifier cv and no arguments; the type T1 is cv T* and R is the return type of the pointer to member function
-- a class type that is derived from std::unary_function<T1, R>

4. The template instantiation reference_wrapper<T> shall be derived from std::binary_function<T1, T2, R> only if the type T is any of the following:
-- a function type or a pointer to function type taking two arguments of types T1 and T2 and returning R
-- a pointer to member function with cv-qualifier cv and taking one argument of type T2; the type T1 is cv T* and R is the return type of the pointer to member function
-- a class type that is derived from std::binary_function<T1, T2, R>

Des paragraphes 3 et 4, il découle que si la class C1 dérive à la fois de std::unary_function<> et std::binary_function<>, alors reference_wrapper<C1> doit dériver de ces deux types.

Le paragraphe 2 nous renvoie à la section 3.3, paragraphe 3.

3. If a call wrapper (3.1) has a weak result type the type of its member type result_type is based on the type T of the wrapper’s target object (3.1):
-- if T is a function, reference to function, or pointer to function type, result_type shall be a synonym for the return type of T;
-- if T is a pointer to member function, result_type shall be a synonym for the return type of T;
-- if T is a class type with a member type result_type, then result_type shall be a synonym for T::result_type;
-- otherwise result_type shall not be defined.

Puisque std::unary_function<> et std::binary_function<> définissent tous les deux le type result_type, C1 hérite de ces types. Dans ce contexte, faire référence à C1::result_type provoque une erreur du à l'ambiguïté. Dans ce contexte, il est normalement impossible de définir reference_wrapper<C1>::result_type.

Je suis rassuré par le fait suivant: l'implémentation du TR1 fournie par Microsoft dans Visual C++ 2008 SP1 butte sur ce problème. L'implémentation présente dans GCC élimine le problème en considérant que C1 ne définit pas de type result_type (ce qui n'est pas illogique, mais la technique de découverte de cette information n'est pas portable de GCC à Visual C++ -- et le compilateur Comeau se comporte encore de manière différente vis-à-vis de ce problème).

Mon échec n'était donc pas si épique. Ou alors, il l'est encore plus que ce que j'imaginais (à savoir que le cas cité n'est même pas censé se présenter dans votre code si vous êtes un tant soit peu respectueux des autres personnes qui travaillent avec vous...).

Commentaires

1. Le lundi, décembre 15 2008, 19:03 par loufoque

A priori détecter que que C1 a un result_type ou pas ça se fait tout simplement avec SFINAE...

2. Le dimanche, février 8 2009, 12:42 par Emmanuel Deloget

Oui, c'est ce qu'il m'avait semblé au début ; mais en fait, C1 hérite du result_type de ses deux classes mères (unary_function::result_type et binary_function::result_type) qui peuvent être différents ; si ils sont identiques, on devrait pouvoir trouver un terrain d'entente (au moins intuitivement) ; si ils sont différents, C1::result_type ne devrait pas être défini, parce qu'aucune définition ne serait correcte (C1::result_type est un symbole ambigue, à moins de l'avoir défini comme étant l'un ou l'autre, voire encore une autre valeur).

Le problème qui se pose est presque cruel : la norme C++ ne dit pas comment je dois me comporter dans ce cas évidemment très spécial. Si C1::result_type n'est pas défini dans C1, alors la définition de reference_wrapper<C1> ne doit pas contenir de result_type non plus (ce qui est un peu génant). Mais si C1 a un result_type défini, alors le result_type de reference_wrapper<C1> existe. Problème : aucun compilateur du marché ne s'accorde sur "comment vérifier que C1 a un result_type à partir du moment ou result_type est défini de manière implicite lorsque C1 hérite de unary_function ET binary_function". Comeau, MSC++ et gcc donnent 3 interprétations différentes (gcc trouve que C1::result_type n'existe pas, comeau annonce une ambiguité, et MSC++ provoque des erreurs incongrues...).

3. Le lundi, février 23 2009, 12:41 par Dodie-76

salut je suis de passage sur ton blog pour te mettre un +5 =) bonne journée

P.S.: je cherche des gens pour faire un échange de +5 quotidien, donc si ça te dit d'avoir environ 30 visites de + sur ton blog, de monter dans le classement boosterblog et donc d'avoir encore plus de visites et de coms, dis-le moi ; )

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

Fil des commentaires de ce billet