¿Cómo puedo especificar claramente qué argumentos estoy pasando y cuáles permanecen predeterminados?

Preguntado por esto : argumento predeterminado en c ++

Digamos que tengo una función como esta: void f(int p1=1, int p2=2, int p3=3, int p4=4);

Y quiero llamarlo usando solo algunos de los argumentos, el rest serán los valores predeterminados.

Algo como esto funcionaría:

 template void f(int p1=1, int p2=2, int p3=3, int p4=4); // specialize: template void f(int p1) { f(1, p1); } template void f(int p1, int p2) { f(1, p1, p2); } // ... and so on. // Would need a specialization for each combination of arguments // which is very tedious and error-prone // Use: f(5); // passes 5 as p2 argument 

Pero requiere demasiado código para ser práctico.

¿Hay una mejor manera de hacer esto?

Utilice el idioma de los parámetros con nombre (→ enlace de preguntas frecuentes ).

La biblioteca Boost.Parameters (→ link ) también puede resolver esta tarea, pero se paga con verbosidad de código y una claridad muy reducida. También es deficiente en el manejo de constructores. Y requiere tener instalada la biblioteca Boost, por supuesto.

Salud y salud,

Echa un vistazo a la biblioteca de parámetros Boost.Parameter .

Implementa parámetros nombrados en C ++. Ejemplo:

 #include  #include  #include  //Define BOOST_PARAMETER_NAME(p1) BOOST_PARAMETER_NAME(p2) BOOST_PARAMETER_NAME(p3) BOOST_PARAMETER_NAME(p4) BOOST_PARAMETER_FUNCTION( (void), f, tag, (optional (p1, *, 1) (p2, *, 2) (p3, *, 3) (p4, *, 4))) { std::cout << "p1: " << p1 << ", p2: " << p2 << ", p3: " << p3 << ", p4: " << p4 << "\n"; } //Use int main() { //Prints "p1: 1, p2: 5, p3: 3, p4: 4" f(_p2=5); } 

Aunque Boost.Parameters es divertido, sufre (desafortunadamente) por una serie de problemas, entre los que destaca la colisión de marcadores de posición (y tener que depurar errores de plantilla / preprocesadores extraños):

 BOOST_PARAMETER_NAME(p1) 

_p1 marcador de posición _p1 que luego usará. Si tienes dos encabezados diferentes que declaran el mismo marcador de posición, obtienes un conflicto. No es divertido.

Hay una respuesta mucho más simple (tanto conceptual como práctica), basada en el Patrón de Builder de alguna manera es el Idioma de Parámetros Nombrados .

En lugar de especificar tal función:

 void f(int a, int b, int c = 10, int d = 20); 

Usted especifica una estructura, en la que anulará el operator() :

  • el constructor se usa para solicitar argumentos obligatorios (no estrictamente en el idioma de los parámetros con nombre, pero nadie dijo que tenía que seguirlo a ciegas), y los valores predeterminados se configuran para los opcionales
  • cada parámetro opcional recibe un setter

En general, se combina con el Encadenamiento que consiste en hacer que los configuradores devuelvan una referencia al objeto actual para que las llamadas se puedan encadenar en una sola línea.

 class f { public: // Take mandatory arguments, set default values f(int a, int b): _a(a), _b(b), _c(10), _d(20) {} // Define setters for optional arguments // Remember the Chaining idiom f& c(int v) { _c = v; return *this; } f& d(int v) { _d = v; return *this; } // Finally define the invocation function void operator()() const; private: int _a; int _b; int _c; int _d; }; // class f 

La invocación es:

 f(/*a=*/1, /*b=*/2).c(3)(); // the last () being to actually invoke the function 

He visto una variante que pone los argumentos obligatorios como parámetros al operator() , esto evita mantener los argumentos como atributos, pero la syntax es un poco más rara:

 f().c(3)(/*a=*/1, /*b=*/2); 

Una vez que el comstackdor ha incorporado todas las llamadas del constructor y los definidores (por lo que se definen aquí, mientras que el operator() no lo está), debería resultar en un código similarmente eficiente en comparación con la invocación de la función “regular”.

Esto no es realmente una respuesta, pero …

En la metaprogtwigción de plantillas C ++ de David Abrahams y Aleksey Gurtovoy (¡publicada en 2004!), Los autores hablan de esto:

Al escribir este libro, reconsideramos la interfaz utilizada para el soporte de parámetros de funciones con nombre. Con un poco de experimentación, descubrimos que es posible proporcionar la syntax ideal mediante el uso de objetos de palabras clave con operadores de asignación sobrecargados:

 f(slew = .799, name = "z"); 

Ellos van a decir:

No vamos a entrar en los detalles de implementación de esta biblioteca de parámetros nombrados aquí; es bastante sencillo que le sugerimos que intente implementarlo usted mismo como un ejercicio.

Esto fue en el contexto de la plantilla de metaprogtwigción y Boost :: MPL. No estoy muy seguro de cómo su implementación “directa” encajaría con los parámetros predeterminados, pero supongo que sería transparente.

    Intereting Posts