2010-05-12 30 views
21

Estoy teniendo un problema con el parámetro de la función opcional en C++Pasando parámetro opcional por referencia en C++

Lo que estoy tratando de hacer es función con parámetro opcional que se pasa por referencia escribir, de modo que pueda Úselo de dos maneras (1) y (2), pero en (2) realmente no me importa cuál es el valor de mFoobar.

que he probado tal código:

void foo(double &bar, double &foobar = NULL) 
{ 
    bar = 100; 
    foobar = 150; 
} 

int main() 
{ 
    double mBar(0),mFoobar(0); 

    foo(mBar,mFoobar);    // (1) 
    cout << mBar << mFoobar; 

    mBar = 0; 
    mFoobar = 0; 

    foo(mBar);      // (2) 
    cout << mBar << mFoobar; 

    return 0; 
} 

pero se estrella en

void foo(double &bar, double &foobar = NULL) 

con el mensaje:

error: default argument for 'double& foobar' has type 'int' 

¿Es posible resolverlo sin la sobrecarga de funciones?

+3

Por supuesto que no - incluso si usted podría * * pasar una referencia NULL, lo ¿Crees que pasaría dentro de la función cuando trataste de asignarle un valor? –

+9

Error de compilación no es un bloqueo. – kennytm

+2

Su código no está "cayendo", está recibiendo un error de compilación. Un bloqueo solo puede ocurrir después de que haya construido completamente su programa e intentado ejecutarlo. –

Respuesta

19

El argumento predeterminado de una referencia (mutable) debe ser un valor l. Lo mejor que puedo pensar, sin sobrecargar, es

static double _dummy_foobar; 
void foo(double &bar, double &foobar = _dummy_foobar) 
+1

Puede valer la pena definir lvalue para el asker. (Esencialmente, e informalmente, "algo a lo que se puede asignar.") – pkh

+2

@pkh Esa definición es común, pero incorrecta. Algunos valores r también se pueden asignar a, por ejemplo, 'std :: string (" hello ") = world', y no se pueden asignar algunos valores l, por ejemplo, matrices C y objetos sin un operador de asignación. (Además, no se pueden asignar const lvalues, por supuesto, pero parece que se entiende bien). – fredoverflow

+4

Esto puede introducir problemas sutiles de seguridad de hilos, si el objeto ficticio se lee alguna vez. – Potatoswatter

25

¿Por qué no puedes usar la función de sobrecarga? Sin duda, es la solución más fácil para su problema?

void foo(double &bar, double &foobar) 
{ 
    bar = 100; 
    foobar = 150; 
} 

void foo(double &bar) 
{ 
    double foobar = 0.0; 
    foo(bar, foobar); 
} 
+12

Puedo, pero solo quería saber si es posible resolver el problema sin sobrecargar. Imagínese despertarse en una isla donde la gente realmente odia la sobrecarga :). – Moomin

+13

@Moomin Supongo que la isla "Odio los parámetros predeterminados" tiene muchos más habitantes ;-) – fredoverflow

+0

la función de sobrecarga debe ser la mejor alternativa a un booleano ficticio que persiste en la clase, especialmente cuando hay otras clases heredadas de la clase base . – deddebme

29

No utilice referencias para los parámetros opcionales. No existe el concepto de referencia NULL: una referencia siempre es un alias de un objeto en particular.

Quizás vea boost::optional o std::experimental::optional. boost::optional incluso está especializado para tipos de referencia.

void foo(double &bar, optional<double &> foobar = optional<double &>()) 
-4

Puede hacerlo de esta manera loca:

void foo(double &bar, double &foobar = (*(new double()))) 

P. S. - Sé que no es agradable, pero es el camino. ¡También asegúrese de no dejar pérdidas de memoria! :))

+1

Incluso si no causó un comportamiento indefinido, ¿cuál sería un posible mecanismo para eliminar de manera segura el parámetro opcional en los casos en que se usó realmente? –

+3

¡hazlo un smart_ptr y listo! – Inverse

+1

No hay forma de evitar una fuga: no se puede decir dentro de la función si la memoria se asignó mediante el argumento predeterminado o no, y no hay forma de que el puntero se encuentre fuera de la función. ¡Eres una manguera (y tienes una fuga)! –

4

Otra forma de hacerlo es utilizar punteros en lugar de referencias. Esto proporciona la semántica que desea sin sobrecargar. (Personalmente, probablemente iría con una sobrecarga.)

void foo(double* bar, double* foobar = 0) 
{ 
    if (bar) *bar = 100; 
    if (foobar) *foobar = 150; 
} 

    // ... 

    foo(&mBar, &mFoobar); 

    // ... 

    foo(&mBar); 

    // ... 
+2

que lleva a un grupo de! = || == nullptr rociado por todo el universo y un gran dolor de cabeza + demora en ponerse a trabajar cuando el programador que lee su biblioteca de 20,000 líneas con nullptr y ** * * ** ** salpicado por todas partes no puede entender a quién pertenece qué, qué necesita ser especificado y lo que no, y tiene un golpe leve. –

+2

Escucho lo que dices, pero lo estás vendiendo más de la cuenta.Como dije, mi preferencia suele ser utilizar la sobrecarga, pero a menudo llamo desde esas funciones públicas sobrecargadas basadas en referencia a una función privada común para hacer el trabajo real, y estoy más que feliz de enviar los indicadores y hacer mis comprobaciones nulas. en. – pkh

2

Aquí hay otra manera loca que no da lugar a pérdidas de memoria, que nunca se debe utilizar en la vida real, pero parece ser compatible con el estándar a primera vista y compila con Visual C++ 2008 & g ++ 3.4.4 bajo Cygwin:

void foo(double &bar, double &foobar = (*((double*)0))) 
{ 
    bar = 100; 
    double* pfoobar = &foobar; 
    if (pfoobar != 0) 
     foobar = 150; 
} 

Para reiterar: no hagas esto! ¡HAY MEJORES OPCIONES! ¡SOBRECARGAR PUEDE SER TU AMIGO! Pero sí, puedes hacerlo si eres tonto y cuidadoso. :)

+1

Las referencias deben designar un objeto en el punto de enlace [ref] (http://stackoverflow.com/questions/4364536/c-null-reference) –

+0

compila, tal vez ... pero ¿qué hace? ¿Has intentado ejecutarlo? esto es ** no ** "compatible con el estándar" en absoluto, por lo que el compilador es libre de tratarlo como UB y hacer absolutamente cualquier cosa, como eliminar lo que debe suceder si no es NULL, o la llamada a la función completa, o explotando tu tostadora ¿a quien le importa? –

+0

Uno, sí, intenté ejecutarlo. Aludí a eso con mi referencia a dos compiladores separados más arriba. Dos, supongo que mea culpa por jugar un poco rápido con el concepto de "cumplimiento de normas" hace casi cinco años. En ningún momento cité el capítulo y el versículo, y en ningún momento dije que era un comportamiento definido. Tres, dije repetidamente que había mejores maneras. Tal vez no vio el texto arriba y debajo del código de muestra que publiqué. Creo que el punto original era bastante claro de que hay muchas cosas malas que se pueden hacer sintácticamente, pero eso no se debe hacer. – CasaDeRobison

1

Es mucho más fácil utilizar un tipo de puntero y configurarlo en NULL que establecer el valor predeterminado/opcional para un parámetro de referencia.

+0

Es mucho más fácil para usted cuando escribe su programa. Es un desorden confuso de "qué se define, quién lo definió, alguien lo definió y luego guardó o entregó la propiedad, ¿tengo que gestionarlo?" para que alguien más intente usarlo. –

+1

Por otro lado, las referencias pueden ser un desorden confuso de "¿esta función de biblioteca va a cambiar mi variable o no?" que puede llevar a problemas de seguridad de hilos, entre otras cosas. Es otro caso de C/C++ que te deja en el barco "evaluar para tu situación particular". Ambas pueden ser herramientas útiles en la caja de herramientas. –

+1

@Bryan Shaw: Es por eso que tienes referencias 'const'. A menos que la función de biblioteca tome una referencia 'const', debe suponer que cambiará su variable. En la mayoría de los casos, las referencias deben preferirse a los punteros. – Sameer

0

Hablando en términos del paradigma orientado a objetos: Si la clase dada tiene y "por defecto", este defecto debe declarada en consecuencia, y entonces puede ser utilizado como un "parámetro predeterminado" Ex:

class Pagination { 
private: 
    int currentPage; 
public: 

    //... 
    Pagination() { 
     currentPage = 1; 
     //... 
    } 

    // your Default Pagination (Must be initialized before thread concurrency) 
    static Pagination& Default() { 
     static Pagination p; 
     return p; 
    } 
}; 

En su método ...

 //... 
    std::vector<User> 
    findByFilter(User& audit, Pagination& p = Pagination::Default()) { 
    // ... 

Editado: esta solución es muy adecuado ya que en este caso se trata de una paginación "global predeterminada" y un único valor de "referencia". También tendrá el poder de cambiar los valores por defecto, tales como navegación preferencias de visualización y/etc

Edit2: ortografía y la fijación ...

Cuestiones relacionadas