Constructeur de type int

Compte tenu du coût, ces cas sont-ils les mêmes?

// case 1 int a = 5; // case 2 int a (5); // case 3 int a; a = 5 

Le premier et le second sont exactement les mêmes car les deux sont l’ initialisation . Le troisième est différent, car c’est une cession . Ces différences sont conformes à la norme C ++. Cependant, le compilateur peut traiter tous les trois comme identiques!

Les trois syntaxes sont différentes, supportez-moi alors que j’utilise un type défini par l’utilisateur au lieu de int, je reviendrai plus tard sur int.

 T a(5); // Direct initialization T b = 5; // Implicit conversion (5->tmp) + copy-initialization T c; c = 5; // Default initialization + assignment 

Dans le premier cas, l’object a est construit à l’aide d’un constructeur prenant un int ou un type pouvant être implicitement converti à partir de int .

 struct T { T( int ); // T a(5) will call this directly }; 

Dans le second cas, un object temporaire de type T est créé par une conversion implicite à partir de int , puis cet object temporaire est utilisé pour copier la construction b . Le compilateur est autorisé à optimiser le code et à effectuer uniquement la conversion implicite à la place de l’object final (au lieu de l’utiliser pour créer le temporaire. Toutefois, toutes les ressortingctions doivent être vérifiées :

 class T { T( T const & ); public: explicit implicit T( int ); }; int main() { T b = 5; // Error 1: No implicit conversion from int to T. // Fix: remove the `explicit` from the constructor // Error 2: Copy constructor is not accessible } 

Le troisième cas est la construction par défaut suivie de l’affectation. Les exigences relatives au type sont qu’il peut être construit par défaut (il existe un constructeur sans argument, ou il n’y a pas de constructeur défini par l’utilisateur et le compilateur le définira implicitement). Le type doit pouvoir être assigné à partir de int ou il doit y avoir une conversion implicite d’ int en un type U pouvant être affecté à T Comme vous le voyez, les exigences pour le type dans les cas d’arborescence sont différentes.

Outre la sémantique des différentes opérations, il existe une autre différence importante: toutes ne peuvent pas être utilisées dans tous les contextes. En particulier, dans une liste d’initialisation d’une classe, vous ne pouvez pas utiliser la version implicite d’initialisation de conversion + copie et vous ne pouvez utiliser que la première moitié de la construction par défaut + l’assignation .

 // OK // error // ok but different struct test { struct test { struct test { T x; T x; T x; test(int v) : x(v) {} test(int v) : x=5 {} test( int v ) { x = v; } 

Dans le premier cas, l’atsortingbut x est directement initialisé avec la valeur v . Le deuxième cas est une erreur de syntaxe. Le troisième cas par défaut est initialisé puis affecté dans le corps du constructeur.

Pour revenir à l’exemple int , toutes les conditions requirejses sont remplies par le type. Il n’y a donc pratiquement aucune différence pour le code généré par le compilateur dans les trois cas, mais vous ne pouvez toujours pas utiliser int b = 5; version dans une liste d’initialisation pour initialiser un atsortingbut entier. De plus, si une classe a un atsortingbut membre qui est un entier constant, vous ne pouvez pas utiliser l’équivalent de int c; c =5; int c; c =5; (troisième colonne ci-dessus) lorsque l’atsortingbut membre devient const lorsqu’il entre dans le bloc constructeur, c’est-à-dire x = v; ci-dessus serait d’essayer de modifier une constante et le compilateur se plaindra.

Quant au coût que chacun a, s’il peut être utilisé, il en résulte le même coût pour un int (pour tout type de POD) mais pas pour les types définis par l’utilisateur qui ont un constructeur par défaut, auquel cas T c; c = 5; T c; c = 5; entraînera le coût de la construction par défaut suivi du coût de la cession . Dans les deux autres cas, la norme indique explicitement que le compilateur est autorisé à générer exactement le même code (une fois les contraintes vérifiées).

Pour les deux premiers, il n’y aura pas de différence.

De la documentation standard, 8.5.11 ,

La forme d’initialisation (entre parenthèses ou =) est généralement insignifiante, mais importe quand l’initialiseur ou l’entité en cours d’initialisation a un type de classe; voir ci-dessous. Un initialiseur entre parenthèses peut être une liste d’expressions uniquement lorsque l’entité en cours d’initialisation a un type de classe.

Le troisième n’est pas une initialisation mais une affectation.

Et compte tenu du coût,

Dans les deux premiers cas, vous créez un entier avec une valeur 5.

Dans le troisième cas, vous créez un entier avec une valeur indéfinie et le remplacez par 5 ..

Si vous utilisez un compilateur d’optimisation, ils seront tous compilés dans le même code. Donc, ils ont tous le même coût.

Oui, ils évaluent tous exactement la même représentation d’assembleur. Vous pouvez le tester, par exemple avec GCC, en écrivant une fonction fictive puis en générant le résultat de l’assembleur: g++ -S file.cxx -o file.s