34 votes

Pourquoi gcc et clang produisent-ils chacun une sortie différente pour ce programme? (opérateur de conversion vs constructeur)

programme:

#include <stdio.h>

struct bar_t {
    int value;
    template<typename T>
    bar_t (const T& t) : value { t } {}

    // edit: You can uncomment these if your compiler supports
    //       guaranteed copy elision (c++17). Either way, it 
    //       doesn't affect the output.

    // bar_t () = delete;
    // bar_t (bar_t&&) = delete;
    // bar_t (const bar_t&) = delete;
    // bar_t& operator = (bar_t&&) = delete;
    // bar_t& operator = (const bar_t&) = delete;
};

struct foo_t {
    operator int   () const { return 1; }
    operator bar_t () const { return 2; }
};

int main ()
{
    foo_t foo {};
    bar_t a { foo };
    bar_t b = static_cast<bar_t>(foo);

    printf("%d,%d\n", a.value, b.value);
}

sortie de gcc 7/8:

2,2

sortie pour clang 4/5 (également pour gcc 6.3)

1,1

Il semble que la suite se passe lors de la création d'instances de l' bar_t:

Pour gcc, il calls foo_t::operator bar_t alors constructs bar_t with T = int.

Pour le bruit, c' constructs bar_t with T = foo_t alors calls foo_t::operator int

Le compilateur est correct ici? (ou peut-être qu'ils sont tous les deux corrects si ce n'est une forme de comportement indéfini)

18voto

bames53 Points 38303

Je crois que clang résultat est correct.

Dans les deux bar_t a { foo } direct de la liste d'initialisation et dans un static_cast entre les types définis par l'utilisateur, les constructeurs du type de destination sont considérés avant défini par l'utilisateur opérateurs de conversion sur le type de source (C++14 [dcl.init.liste]/3 [expr.statique.distribution]/4). Si la résolution de surcharge trouve un constructeur approprié alors qu'il est utilisé.

Quand on fait de la résolution de surcharge bar_t::bar_t<foo_t>(const foo_t&) est viable et sera un meilleur match que celui de tout à l'instanciation de ce modèle résultant de l'utilisation des opérateurs de cast sur le foo. Il sera également mieux que n'importe quel défaut a déclaré constructeurs depuis qu'ils prennent autre chose que de l' foo_t, alors bar_t::bar_t<foo_t> est utilisé.


Le code tel qu'il est actuellement rédigé dépend de C++17 garantis copie élision; Si vous compilez sans C++17 garanti copie élision (par exemple, -std=c++14) alors clang ne rejeter le présent code en raison de la copie de l'initialisation en bar_t b = static_cast<bar_t>(foo);.

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