Question

struct X
{
   X():mem(42){}
   void f(int param = mem) //ERROR
   {
      //do something
   }
private: 
   int mem;
};

Quelqu'un peut-il me donner une seule raison pour expliquer pourquoi cela est illégal en C ++ ?! C'est-à-dire, je sais que c'est une erreur, je sais ce que les moyens d'erreur, je ne peux pas comprendre pourquoi serait-ce illégal!

Était-ce utile?

La solution

Votre code (simplifié):

struct X
{
   int mem;
   void f(int param = mem); //ERROR
};

Vous souhaitez utiliser des données non membres statiques comme valeur par défaut pour un paramètre d'une fonction de membre. La première question qui vient à l'esprit est: qui instance spécifique de la classe la valeur par défaut mem appartient à

X x1 = {100};  //mem = 100
X x2 = {200};  //mem = 200

x1.f(); //param is 100 or 200? or something else?

Votre réponse est peut-être 100 comme f() est invoquée sur le x1 objet qui a mem = 100. Si oui, alors il faut la mise en œuvre à mettre en œuvre f() comme:

void f(X* this, int param = this->mem);

qui exige à son tour le premier argument à initialiser avant de l'initialisation d'un autre argument. Mais la norme C de ne spécifie pas d'ordre d'initialisation des arguments de la fonction. Par conséquent non autorisé. Son pour la même raison que C ++ standard ne permet même pas ceci:

int f(int a, int b = a); //§8.3.6/9

En fait, §8.3.6 / 9 dit explicitement,

  

arguments par défaut sont évalués chaque   temps la fonction est appelée. L'ordre   d'évaluation des arguments de la fonction est   Non spécifié . Par conséquent, les paramètres   d'une fonction ne doit pas être utilisé dans   expressions d'argument par défaut , même si   ils ne sont pas évalués.

Et reste de la section est une lecture intéressante.


Un sujet intéressant lié à des arguments « par défaut » (non liés à ce sujet cependant):

Autres conseils

arguments par défaut doivent être connus au moment de la compilation. Quand vous parlez de quelque chose comme un appel de fonction, la fonction est connue au moment de la compilation, même si la valeur de retour n'est pas, de sorte que le compilateur peut générer ce code, mais quand vous avez par défaut à une variable membre, le doesn du compilateur » sais où trouver cette instance à la compilation, ce qui signifie qu'il aurait effectivement de passer un paramètre (this) pour trouver mem. Notez que vous ne pouvez pas faire quelque chose comme void func(int i, int f = g(i)); et les deux sont effectivement la même restriction.

Je pense aussi que cette restriction est stupide. Mais alors, C ++ est pleine de restrictions stupides.

Comme DeadMG a mentionné ci-dessus, comme somethig

void func(int i, int f = g(i))

est illégal pour la même raison. je suppose, cependant, que ce n'est pas simplement une restriction idiote. Pour permettre une telle construction, nous devons limiter l'ordre d'évaluation des paramètres de fonction (comme nous devons calculer cela avant this-> mem), mais le standard C ++ décline explicitement toute hypothèse sur l'ordre d'évaluation.

La réponse acceptée dans la double question est pourquoi, mais la norme aussi précise explicitement pourquoi il en est ainsi:

8.3.6 / 9:

» Exemple :. La déclaration de X :: mem1 () dans l'exemple suivant est mal formé parce qu'aucun objet est fourni pour le X :: membre non statique a utilisé comme initialiseur

int b;
class X
  int a;
  int mem1(int i = a);    // error: nonstatic member a
                          // used as default argument
  int mem2(int i = b);    // OK: use X::b
  static int b;
};

La déclaration de X :: mem2 () est significatif, cependant, car aucun objet est nécessaire pour accéder à l'élément statique X :: b. Les classes, les objets et les éléments sont décrits dans la clause 9. «

... et puisqu'il n'y existe pas de syntaxe pour fournir l'objet nécessaire pour résoudre la valeur de X::a à ce moment-là, il est effectivement impossible d'utiliser des variables membres non statiques comme initialiseurs pour les arguments par défaut.

section de l'ISO C 8.3.6 / 9

  

un membre non statique ne doit pas être utilisé dans une expression d'argument par défaut , même si elle   non exploité, sauf si elle apparaît comme l'id-expression d'une expression d'accès au membre de la classe (5.2.5) ou si elle est utilisée pour former un pointeur vers un membre (5.3.1).

Consultez également l'exemple donné dans cette section.

Pour une raison, parce que f est publique, mais mem est privé. En tant que tel, le code comme ceci:

int main() { 
    X x;
    x.f();
    return 0;
}

... impliquerait un code externe de récupération des données privées de X.

En dehors de cela, il (ou au moins pourrait) faire également la génération de code un peu délicat. Normalement, si le compilateur va utiliser un argument par défaut, il obtient la valeur qu'il va passer dans le cadre de la déclaration de fonction. Génération de code pour transmettre cette valeur comme paramètre est triviale. Lorsque vous pourriez passer un membre d'un objet (peut-être imbriqué de façon arbitraire en profondeur), puis ajouter des choses comme la possibilité qu'il soit un nom dépendant dans un modèle, qui pourrait (par exemple) nommer un autre objet avec une conversion à la cible correcte entrez, et vous avez une recette pour faire la génération de code assez difficile. Je ne suis pas sûr, mais je soupçonne que quelqu'un a pensé à des choses comme ça, et décidé qu'il était préférable de rester prudent, et peut amincit ouvrir plus tard, si une bonne raison a été trouvé pour le faire . Compte tenu du nombre de fois où je l'ai vu des problèmes surgissent de là, je suppose que ça va rester comme ça pendant longtemps, tout simplement parce qu'il provoque rarement des problèmes.

compilateur doit connaître les adresses pour maintenir les valeurs par défaut au moment de la compilation. Adresses des variables membres non statiques sont inconnues au moment de la compilation.

Comme toutes les autres réponses discutent juste le problème, je pensais que je posterais une solution.

Comme utilisé dans d'autres langues sans arguments par défaut (par exemple C # 4.0 pre)

Utilisez simplement la surcharge de fournir le même résultat:

struct X
{
   X():mem(42){}
   void f(int param)
   {
      //do something
   }
   void f()
   {
      f(mem);
   }
private: 
   int mem;
};

arguments par défaut sont évalués en deux étapes distinctes, dans des contextes différents.
Tout d'abord, le nom recherche pour l'argument par défaut est effectuée dans le contexte de la déclaration.
En second lieu, l'évaluation de l'argument par défaut est effectuée dans le cadre de l'appel de fonction réelle.

Pour garder la mise en œuvre de devenir trop compliqué, certaines restrictions sont appliquées aux expressions qui peuvent être utilisés comme arguments par défaut.

  • Variables à durée de vie non statique ne peuvent pas être utilisés, car ils pourraient ne pas exister au moment de l'appel.
  • variables membres non statiques ne peuvent pas être utilisés, car ils ont besoin d'un (implicite) de qualification this->, qui peut généralement pas être évalué sur le site d'appel.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top