Appel en boucle d'un constructeur

L'ECM est vraiment complet et minimal!

Le problème exposé dans ce sujet a été résolu.

Bonjour,

J’ai un comportement que je n’arrive pas à expliquer. Par chance, j’ai réussi à réaliser un ECM qui met en évidence ce problème en un minimum de lignes.

Je souhaite avoir une classe et deux constructeurs: l’un sans parametre (ie. void), qui va appeler le second en passant un paramètre par défaut1. Dans mon ECM, la classe Foo se comporte parfaitement comme attendue, mais Bar appelle en boucle le premier constructeur. La seule différence : le type du paramètre. Voyez le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <unistd.h>
#include <stdio.h>

#define PRINT(msg) do {printf("%s", msg); usleep(200*1000);} while(0);

class Foo {
public:
    Foo(void) {
        PRINT("Foo 1 pre\n");
        Foo(1);
        PRINT("Foo 1 post\n");
    }

    Foo(int i) {
        PRINT("Foo 2\n");
    }
};

class Bar {
public:
    typedef enum _ {
        EA,
        EB,
    } myenum;

    Bar(void) {
        PRINT("Bar 1 pre\n");
        Bar(EA);
        PRINT("Bar 1 post\n");
    }

    Bar(myenum i) {
        PRINT("Bar 2\n");
    }
};

int main(void)
{
    Foo f;
    Bar b;

    return 0;
}
1
2
3
4
5
6
7
8
9
$ ./a.out 
Foo 1 pre
Foo 2
Foo 1 post
Bar 1 pre
Bar 1 pre
Bar 1 pre
Bar 1 pre
...

Une explication ?

Merci d’avance pour vos réponses.


  1. hum, je devrait peut-être utiliser un seul constructeur et mettre une valeur par défault directement dans le prototype … 

Je n’ai pas regardé, en détail pourquoi ça ne marche pas pour Bar, je ne peux pas d’ici (je n’ai pas de compilo).

La différence que je vois est que tu utilises une enum, j’avoue que je ne me souviens plus comment sont gérés le typage des enum en C++.

Mais par contre, le comportement que tu essaye de faire, cela correspond aux valeurs par défaut, pourquoi ne pas tout simplement faire :

1
2
3
4
5
    Foo(int i=1) {

    }
    Bar(myenum i=EA) {
    }

EDIT : ah, bah je viens de voir ta footnote, du coup, tu avais déjà remarqué :p

hum, je devrait peut-être utiliser un seul constructeur et mettre une valeur par défault directement dans le prototype …

+0 -0

Je ne suis pas un expert du C++. Et ce cas est très bizarre.

Edit : Explication foireuse …

+0 -0

Lu’!

Je souhaite avoir une classe et deux constructeurs: l’un sans parametre (ie. void), qui va appeler le second en passant un paramètre par défaut.

Nodraak
  1. Pour ta footnote, oui, très clairement.
  2. Ton actuel ne fait pas ça, il crée un objet temporaire dans le constructeur qui est détruit immédiatement. Utilise la liste d’initialisation, c’est son boulot (et ça corrige l’exécution en boucle).

Par contre, expliquer le problème pour l’instant je sèche, même si je regarde.

Pour une raison que j’ignore, la construction du temporaire avec Bar{EA} au lieu de Bar(EA) produit le comportement voulu.

Après, de la à savoir pourquoi gcc (et clang, j’ai testé) décident d’appeler le constructeur par défaut avec le code d’origine, mystère.

Un autre indice, le compilateur prend l’instruction pour une … déclaration :

1
2
3
4
5
6
7
8
9
struct Bar {
    enum e { A, B };
    Bar(){ Bar(A); }
};

int main(void){
    Bar b;
    return 0;
}

compile ^^ .

Et VS nous dit :

c:...\main.cpp(3): warning C4717: ’Bar::Bar’ : récurrent sur tous les chemins d’accès de contrôle, la fonction entraînera un dépassement de capacité de la pile d’exécution.

A noter, comme c’est un UB :

  • GCC segfault
  • Clang optimise en virant l’appel
  • VS stack overflow.

EDIT :

Merci @REMqb pour l’info, c’est dû au parsing. Le compilateur croit que A, est le nom de l’objet que l’on déclare de type Bar. D’où l’appel récursif infini. Moralité, n’utilisez ces f*cking parenthèses pour initialiser que quand vous n’avez pas d’autre choix et utilisez la liste d’initialisation.

https://stackoverflow.com/questions/27176956/error-constructing-temporary-object-whose-constructor-takes-a-single-enum-parame

Ca m’étonne que le compilateur ne sorte pas un warning disant qu’on redéclare EA avec un type différent …

Nodraak

tu ne redéclares pas EA, tu crées un objet temporaire sans nom. Dans ce contexte-ci, Bar(EA); est identique à Bar variable(EA); (sans avoir le warning "variable non utilisée")

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte