c++ - todas - ¿Es posible usar la función de llamada de miembro como argumento predeterminado?
tipos de parametros en c++ (3)
Aquí está mi código:
struct S
{
int f() { return 1; }
int g(int arg = f()) { return arg; }
};
int main()
{
S s;
return s.g();
}
Esto no se compila con el error:
error: cannot call member function ''int S::f()'' without object
Intentar this->f()
tampoco funciona, ya que no se puede usar en ese contexto.
¿Hay alguna manera de hacer que esto funcione, aún usando el argumento predeterminado?
Por supuesto, se puede evitar evitando los argumentos predeterminados:
int g(int arg) { return arg; }
int g() { return g(f()); }
sin embargo, esto se vuelve detallado considerando que en el "código real" hay más parámetros antes de arg
, y varias funciones siguiendo este patrón. (Y aún más feo si hubiera múltiples argumentos predeterminados en la función).
NÓTESE BIEN. Esta pregunta parece similar al principio, pero en realidad está preguntando cómo hacer un cierre, que es un problema diferente (y la solución vinculada no se aplica a mi situación).
Agrego otra respuesta, que es completamente diferente de la anterior y podría resolver su problema.
La idea es usar otra clase y la combinación correcta de constructores explícitos y no explícitos.
Sigue un mínimo, ejemplo de trabajo:
#include <functional>
#include <iostream>
template<class C, int(C::*M)()>
struct Arg {
std::function<int(C*)> fn;
Arg(int i): fn{[i](C*){ return i; }} { }
explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { }
};
struct S {
int f() { return 1; }
int h() { return 2; }
void g(int arg0,
Arg<S, &S::f> arg1 = Arg<S, &S::f>{},
Arg<S, &S::h> arg2 = Arg<S, &S::h>{})
{
std::cout << "arguments" << std::endl;
std::cout << "arg0: " << arg0 << std::endl;
std::cout << "arg1: " << arg1.fn(this) << std::endl;
std::cout << "arg2: " << arg2.fn(this) << std::endl;
}
};
int main() {
S s{};
s.g(42, 41, 40);
s.g(0);
}
El ejemplo muestra cómo puede mezclar parámetros predeterminados y no predeterminados.
Es bastante sencillo modificarlo y dejar que g
sea una función con una lista de argumentos vacía, como en la pregunta original.
También estoy bastante seguro de que uno puede refinar el ejemplo y terminar con algo mejor que eso, de todos modos debería ser un buen punto de partida.
Sigue la solución aplicada al ejemplo original de la pregunta:
#include <functional>
template<class C, int(C::*M)()>
struct Arg {
std::function<int(C*)> fn;
Arg(int i): fn{[i](C*){ return i; }} { }
explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { }
};
struct S {
int f() { return 1; }
int g(Arg<S, &S::f> arg = Arg<S, &S::f>{}) {
return arg.fn(this);
}
};
int main() {
S s{};
return s.g();
}
Y eso es todo, es posible hacerlo, incluso sin métodos static
o variables globales.
Por supuesto, podemos usar nuestro esto de alguna manera. Se trata de doblar un poco el idioma ...
Si se le permite usar funciones experimentales de C ++ 17, puede usar std::optional
desde la STL (consulte here para obtener más detalles).
En otros términos algo como:
int g(std::optional<int> oarg = std::optional<int>{}) {
int arg = oarg ? *oarg : f();
// go further
}
EDITAR
Como se sugiere en los comentarios, el código anterior debe ser lógicamente equivalente al siguiente:
int g(std::optional<int> oarg = std::optional<int>{}) {
int arg = oarg.value_or(f());
// go further
}
Este es un poco más legible (¿no es así?), Pero tenga en cuenta que se ejecuta f
en cualquier caso.
Si esa función es costosa, tal vez no valga la pena.
Solo puedes usar miembros allí si son static
. De un borrador de C ++ 11 (n3299), §8.3.6 / 9:
De manera similar, un miembro no estático no se usará en un argumento predeterminado, incluso si no se evalúa, a menos que aparezca como la expresión-id de una expresión de acceso de miembro de clase (5.2.5) o a menos que se use para formar un puntero al miembro (5.3.1).
Por ejemplo, esto funciona:
struct S {
static int f() { return 1; }
int g(int arg = f()) { return arg; }
};
int main()
{
S s;
return s.g();
}
Esto también funciona (creo que eso es lo que significa la primera expresión):
struct S {
int f() { return 42; }
int g(int arg);
};
static S global;
int S::g(int arg = global.f()) { return arg; }
int main()
{
S s;
return s.g();
}
En cuanto a this
, de hecho no está permitido (§8.3.6 / 8):
La palabra clave
this
no se utilizará en un argumento predeterminado de una función miembro.
La página de argumentos por defecto en cppreference.com tiene muchos detalles sobre el tema, puede ser bastante complejo.