Elad Weiss
Según entiendo, std::invoke
me permite hacer algo como:
std::invoke(f, arg1, arg2, ...);
¿Hay algún escenario en el que sea más ventajoso que simplemente hacer:
f(arg1, arg2, ...);
StoryTeller – Unslander Mónica
Si el invocable es un puntero a una función miembro, entonces debe hacer uno de estos:
(arg1->*f)(arg2,...);
(arg1.*f)(arg2,...);
Dependiendo de qué arg1
es.
INVOCAR (y su contraparte oficial de la biblioteca std::invoke
) fue más o menos diseñado para simplificar tales líos.
usarías std::invoke
para ayudar a la persona que llama a pasar su código ningún invocables, y no tener que adaptar su sitio de llamadas con una lambda o una llamada a std::bind
.
-
Bonito, ya veo. Supongo que eso también significa que sin invocar necesitaré un código diferente para una función miembro y una función libre. ¿Está bien?
– Elad Weiss
24 de septiembre de 2017 a las 9:27
-
@EladWeiss – Sí. Tuvimos que escribir un montón de especializaciones o volver a implementar INVOKE nosotros mismos (e imaginar también admitir punteros inteligentes, yikes). La nueva utilidad es una herramienta de escritores de bibliotecas.
– StoryTeller – Unslander Mónica
24 de septiembre de 2017 a las 9:29
-
@EladWeiss Echa un vistazo a un implementación de muestra de
std::invoke
y luego agradezca que no tiene que escribir eso usted mismo: P– Rakete1111
24 de septiembre de 2017 a las 9:30
-
@ Rakete1111 ahora imagina cómo se vería si
if constexpr
no estaba disponible.– Revólver_Ocelote
24 de septiembre de 2017 a las 9:53
-
@ Rakete1111 Tenga en cuenta que hicimos trampa con esa implementación al asumir la disponibilidad de
invoke_result_t
yis_nothrow_invocable
; el primero es particularmente crucial para hacerinvoke
Amigable con SFINAE. En la práctica, es probable que funcionen con la misma maquinaria subyacente, que debería ser más compleja. @StoryTeller correcto.– CT
24 de septiembre de 2017 a las 10:28
std::invoke
puede ser útil cuando crea una lambda y necesita llamarla de inmediato. Si la lambda es grande, los paréntesis después pueden ser difíciles de observar:
[] (/* args */) {
// many lines here
// ...
} (/* args */)
contra
std::invoke(
[] (/* args */) {
// many lines here
// ...
},
/* args */);
Tratando de agregar además de las dos respuestas, una está dando una buena explicación teórica y la otra está tratando de proporcionar un ejemplo concreto. Aquí hay una buena razón de por qué std::invoke
hace las cosas mejor.
#include <functional>
#include <iostream>
struct foo
{
void echo(){std::cout << "foo" << std::endl;};
};
int main()
{
////
// Vanilla version of invoking .echo()
foo(f);
f.echo();
////
// Pointer to *class* member function version
void (foo::* pf)() = &foo::echo;
foo obj;
(obj.*pf)(); // ugly
// instead do ...
std::invoke(pf, obj);
// obj->echo(); <-- does not compile
return 0;
}
-
Ahora, la pregunta de por qué usaría/necesitaría un puntero a una función de miembro de clase es un tema diferente.
– gonidelis
7 jun a las 18:19
#include <iostream>
#include <functional>
template< class Callable, typename ... Args>{}
//f(args...); // if arg1 is a class object pointer
// we should use it like this(arg1.*f)(args...);
std::invoke(f, args...); // now every thing is ok
}
void foo(char c) {
std::cout << "foo called\n";
}
int main()
{
struct S {
int f1(char c) {
std::cout << "S::f1 called\n";
}
void operator()(char c) {
std::cout << "S::operator() called\n";
}
};
int (S:: * fptr)(char c) = &S::f1;
S obj;
dosomething(fptr, obj, 'a');
return 0;
}