web-dev-qa-db-fra.com

Programme compilé différemment dans 3 compilateurs C ++ majeurs. Laquelle a raison?

Comme un suivi intéressant (pas d'une grande importance pratique cependant) à ma question précédente: Pourquoi C++ nous permet-il d'entourer le nom de la variable entre parenthèses lors de la déclaration d'une variable?

J'ai découvert que la combinaison de la déclaration entre parenthèses avec la fonction nom de classe injecté peut conduire à des résultats surprenants concernant le comportement du compilateur.

Jetez un œil au programme suivant:

#include <iostream>
struct B
{
};

struct C
{
  C (){ std::cout << "C" << '\n'; }
  C (B *) { std::cout << "C (B *)" << '\n';}
};

B *y = nullptr;
int main()
{
  C::C (y);
}
  1. Compiler avec g ++ 4.9.2 me donne l'erreur de compilation suivante:

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
    
  2. Il se compile avec succès avec MSVC2013/2015 et imprime C (B *)

  3. Il se compile avec succès avec clang 3.5 et affiche C

La question obligatoire est donc laquelle a raison? :)

(Cependant, je me suis fortement incliné vers la version clang et la manière msvc d'arrêter de déclarer la variable après avoir simplement changé de type avec techniquement son typedef semble un peu bizarre)

116
Predelnik

GCC est correct, au moins selon les règles de recherche C++ 11. 3.4.3.1 [class.qual]/2 spécifie que, si le spécificateur de nom imbriqué est le même que le nom de classe, il fait référence au constructeur et non au nom de classe injecté. Il donne des exemples:

B::A ba;           // object of type A
A::A a;            // error, A::A is not a type name
struct A::A a2;    // object of type A

Il semble que MSVC l'ait mal interprété comme une expression de style de fonction créant un C temporaire avec y comme paramètre constructeur; et Clang l'interprète comme une déclaration d'une variable appelée y de type C.

91
Mike Seymour

G ++ est correct car il donne une erreur. Parce que le constructeur ne pouvait pas être appelé directement dans un tel format sans l'opérateur new. Et bien que votre code appelle C::C, il ressemble à un appel constructeur. Cependant, selon la norme C++ 11 3.4.3.1, il ne s'agit pas d'un appel de fonction légal, ni d'un nom de type ( voir la réponse de Mike Seymour ).

Clang a tort car il n'appelle même pas la fonction correcte.

MSVC est quelque chose de raisonnable, mais il ne suit toujours pas la norme.

16
Kun Ling