22 votes

Régression possible dans G++ 6.1.0

Le code suivant

#include <string>
#include <map>
#include <cassert>

    struct       AV {
        explicit AV(std::string const&) {}
    };

#if 1
    static void check_cache_item(
        std::map<std::string, std::string> const& items) // FIXME remove
    {
        assert(!items.empty());
    }
#endif
    static void check_cache_item(
        std::map<std::string, AV> const& items)
    {
        assert(!items.empty());
    }

int main()
{
    check_cache_item({ { "id", "0" }, { "pk", "#0" } });
    check_cache_item({ { "id", "0" }, { "pk", "#1" } });
    check_cache_item({ { "id", AV{"0"} }, { "pk", AV{"#1"} } });
}

est accepté par g++ 4.8.4, g++ 5.3.0, clang++ 3.9.0 ; mais g++ 6.1.0 donne une erreur :

cci.cc: In function ‘int main()’:
cci.cc:25:55: error: call of overloaded ‘check_cache_item(<brace-enclosed initializer list>)’ is ambiguous
     check_cache_item({ { "id", "0" }, { "pk", "#0" } });
                                                       ^
cci.cc:10:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)
     static void check_cache_item(
                 ^~~~~~~~~~~~~~~~
cci.cc:16:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, AV>&)
     static void check_cache_item(
                 ^~~~~~~~~~~~~~~~
cci.cc:26:55: error: call of overloaded ‘check_cache_item(<brace-enclosed initializer list>)’ is ambiguous
     check_cache_item({ { "id", "0" }, { "pk", "#1" } });
                                                       ^
cci.cc:10:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)
     static void check_cache_item(
                 ^~~~~~~~~~~~~~~~
cci.cc:16:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, AV>&)
     static void check_cache_item(
                 ^~~~~~~~~~~~~~~~
cci.cc: At global scope:
cci.cc:10:17: warning: ‘void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)’ defined but not used [-Wunused-function]
     static void check_cache_item(
                 ^~~~~~~~~~~~~~~~

Si je #ifdef le premier constructeur, tous les compilateurs lancent une erreur (à juste titre, car le constructeur AV est explicite).

S'agit-il d'une régression dans G++ 6.1.0 ?

14voto

ecatmur Points 64173

Il s'agit d'un aspect surprenant et quelque peu regrettable de la norme (j'irais même jusqu'à parler de défaut) ; il résulte d'une collision entre les règles de résolution des surcharges pour l'initialisation des listes de copies ( [over.match.list] comme le confirme la CWG 1228 ), et le constructeur de transfert d'éléments de pair (conformément à la n4387 ).

gcc (>= 6.1.0) a raison de rejeter votre programme ; clang a tort de l'accepter. Les versions antérieures de gcc acceptent votre programme parce qu'elles n'avaient pas encore implémenté n4387 ; clang accepte votre programme parce qu'il exclut les constructeurs explicites de la prise en compte de la résolution de la surcharge pour l'initialisation de la liste de copies , ce qui constitue une violation de l'article [over.match.list] conformément à la norme ( Appeler un constructeur explicite avec une liste braced-init : ambigu ou non ? )


Si nous éliminons les aspects superflus de votre programme, nous en arrivons à une simple question de résolution de la surcharge :

struct A { explicit A(int, int); };
struct B { B(int, int); };

void f(A);
void f(B);

int main() { f({0, 0}); }

Ici A se substitue à pair<std::string const, AV> y B se substitue à pair<string const, string> . Le constructeur de A est explicite parce que le rythme n4387 implique le constructeur explicite de AV ; mais selon le CWG 1228, les règles pour l'initialisation de la liste des copies :

[...] incluent tous les constructeurs mais déclarent que le programme est mal formé si un constructeur explicite est sélectionné par la résolution de surcharge. [...]

[over.match.list] :

[...] Lors de l'initialisation d'une liste de copies, si un fichier explicit est choisi, l'initialisation est mal formée. [ Remarque : Ceci diffère des autres situations ([over.match.ctor], [over.match.copy]), où seuls les constructeurs convertisseurs sont pris en compte pour l'initialisation de la copie. Cette restriction ne s'applique que si cette initialisation fait partie du résultat final de la résolution de la surcharge. - note de fin ]

Par conséquent, votre programme est considéré à juste titre (en vertu de la norme telle qu'elle existe actuellement) comme ambigu.

Pour en savoir plus : Que pourrait-il se passer si l'initialisation par copie de liste permettait l'utilisation de constructeurs explicites ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X