Comme 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 de nom de classe injectée 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);
}
La compilation avec g ++ 4.9.2 me donne l'erreur de compilation suivante:
main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
Il se compile avec succès avec MSVC2013 / 2015 et imprime
C (B *)
Il se compile avec succès avec clang 3.5 et imprime
C
La question obligatoire est donc laquelle a raison? :)
(Je me suis fortement incliné vers la version clang et la manière msvc d'arrêter de déclarer une variable après avoir simplement changé de type avec techniquement son typedef semble un peu bizarre)
la source
C::C y;
n'a pas de sens, non? Ni neC::C (y);
Au début , je pensais que c'était une instance de plus-vexatoire-Parse stackoverflow.com/questions/tagged/most-vexing-parse , mais maintenant je pense que ce comportement est tout simplement indéfini qui signifie trois compilateurs sont « droit ».C::C
ne nomme pas un type, il nomme une fonction, donc GCC a raison imo.Réponses:
GCC est correct, du 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:
Il semble que MSVC l'interprète mal comme une expression de conversion de style fonction créant un temporaire
C
avecy
comme paramètre de constructeur; et Clang l'interprète mal comme une déclaration d'une variable appeléey
de typeC
.la source
A::A a;
, les noms de fonctions devraient être ignorés - ou pas?C (B *)
" .G ++ est correct car il donne une erreur. Parce que le constructeur ne pouvait pas être appelé directement dans un tel format sans
new
opérateur. Et bien que votre code appelleC::C
, cela ressemble à un appel de 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.
la source
new
opérateur?new B(1,2,3)
c'est une sorte d '"appel direct au constructeur" (ce qui, bien sûr, ce n'est pas le cas) par opposition à l'instanciation temporaireB(1,2,3)
ou à la déclarationB b(1,2,3)
.new B(1,2,3)
c'est?new
, un nom de type et une liste d'arguments de constructeur. Ce n'est toujours pas un "appel direct au constructeur".C::C (y);
commeC::C y;
, c'est-à-dire une définition d'une variable y de type C (en utilisant le type C injecté: : C en ignorant à tort la spécification de langage de plus en plus insensée 3.4.1,2 qui fait de C :: C le constructeur). Ce n'est pas tout à fait une erreur flagrante comme vous semblez le penser, imo.