2010-07-06 8 views
2

Je dois ajouter un défaut argument de int& (ex iNewArgument) à une définition de fonction existante dans un fichier d'en-tête:Comment utiliser un paramètre int avec une valeur par défaut?

Quelle est la syntaxe pour initialiser le dernier argument de lui faire prendre une valeur par défaut?

.. 
virtual int ExecNew 
    (int& param1, int& RowIn,int& RowOut, char* Msg = '0', 
      bool bDebug=true, int& iNewArgument = NULL) = 0 ; 
. 
. 

NULL est #defined à 0

Error: default argument: cannot convert from int to int&

+3

Si vous voulez qu'il soit null, alors vous avez besoin d'un pointeur et non d'une référence. –

+0

Je suis avec Mike à ce sujet. Voir [ma réponse] (http://stackoverflow.com/questions/3190571/c-int-reference-question/3190603#3190603). – sbi

+0

Je le reprends (et j'ai supprimé ma réponse). Sauf pour Johannes tout le monde a négligé le «virtuel». Ce n'est pas une bonne idée d'avoir des arguments par défaut pour les fonctions 'virtual'. – sbi

Répondre

9

Votre exemple indique que vous créez une fonction virtuelle. Les arguments par défaut dans les fonctions virtuelles (particulièrement pures) ne sont pas une bonne idée, car ils ne sont pas pris en compte lorsque vous appelez une fonction surchargée.

struct A { 
    virtual void f(int = 4711) = 0; 
}; 

struct B : A { 
    virtual void f(int) { /* ... */ } 
}; 

Maintenant, si quelqu'un appelle f sur un objet B, il a de fournir l'argument - la valeur par défaut est pas utilisé. Vous devrez dupliquer l'argument par défaut dans la classe dérivée, ce qui crée des redondances moche. Au lieu de cela, vous pouvez utiliser le modèle d'interface non-virtuelle

struct A { 
    void f(int a) { 
    int out; 
    f(a, out); 
    } 

    void f(int a, int &out) { 
    doF(a, out); 
    } 

protected: 
    virtual void doF(int a, int out&) = 0; 
}; 

L'utilisateur peut maintenant appeler f avec un, et avec deux arguments, quel que soit le type de classe qu'il appelle la fonction, et qui mettent en place A ne pas s'inquiéter de l'argument par défaut. En utilisant la surcharge au lieu des arguments par défaut, vous n'avez pas besoin de vous casser la tête autour des valeurs ou des temporaires.

+0

Oh, tout le monde a oublié ça! Bonne prise, Johannes, '+ 1' de moi. (Et, BTW, c'est encore plus grave si 'B :: f()' a un argument par défaut différent - le code compile silencieusement, mais a une sémantique différente! – sbi

1

Vous ne pouvez pas faire cela. L'INewArgument doit être un int raw ou une référence const. Une référence non-const ne peut pas être utilisée comme vous essayez de l'utiliser.

1

En bref, vous allez devoir retravailler votre problème. Vous pouvez soit rendre votre int & un int normal et perdre le comportement de référence (être capable de transmettre la valeur modifiée) ou abandonner ayant une valeur par défaut.

+0

En regardant à nouveau votre code d'exemple, ce que vous essayez de faire avec INewArgument est de passer un pointeur, pas une référence. C'est votre autre option dans ce cas: passez un pointeur plutôt qu'une référence. Cela fonctionne bien si cela ne vous dérange pas avec des pointeurs. –

1

La valeur par défaut doit être lvalue pour un type de référence non const, ce que vous essayez de faire ci-dessus ne fonctionnera pas. Qu'est-ce que vous pouvez faire est:

namespace detail { 
    int global; 
} 

void foo(int &x = detail::global) {} 

ou

void bar(const int &x = int(3)) {} 
+0

Pour votre premier cas: 'NULL' comme" Je n'ai pas de paramètre pour vous "est universellement compris,' detail :: global' ne l'est pas. Et pour le second cas: les built-ins doivent être pris par copie, pas par référence 'const'. – sbi

+0

La seconde variante est toujours utile pour les types de données plus complexes, par ex. void foo (const std :: set & x = std :: set ()), ce qui est parfois logique. – Staffan

4

La seule façon significative à fournir un argument par défaut à une référence non-const est de déclarer un objet autonome avec une durée de stockage statique et l'utiliser comme un argument par défaut

int dummy = 0; 

... 
virtual int ExecNew(/* whatever */, int& iNewArgument = dummy) = 0; 

Que ce soit pour vous ou non, c'est à vous de décider.

Dans la fonction, si vous avez besoin de détecter si l'argument par défaut a été utilisé, vous pouvez utiliser la comparaison d'adresse

if (&iNewArgument == &dummy) 
    /* Default argument was used */ 
+0

Si, à l'intérieur de la fonction, vous comparez des adresses, cela indique que la fonction doit prendre cet objet par référence. Si les adresses sont importantes pour la sémantique de la fonction, cela doit être visible pour l'appelant. – sbi

+0

@sbi: Dans ce cas particulier, l'adresse est considérée comme un détail d'implémentation sans signification d'interface. Il serait incorrect d'exposer ce détail d'implémentation à l'utilisateur. Cependant, j'ajouterais simplement que dans le meilleur des cas, l'argument par défaut devrait être choisi de sorte que (la fonction soit conçue de telle sorte que) la fonction n'a pas besoin de détecter le fait que l'argument par défaut a été utilisé. – AnT

1

Vous dites NULL est #defined à 0, il est donc une valeur littérale. Le paramètre est une référence à un int, ce qui signifie que la fonction serait en mesure de changer la valeur. Comment changez-vous la valeur d'un littéral?

Vous devez en faire une référence const, ou ne pas en faire une référence du tout.

Questions connexes