c++ - quitar - como poner spotify como reproductor predeterminado
¿Cuál es la convención de llamada predeterminada de una función lambda de C++? (2)
El siguiente código fue compilado con VC ++ 2012:
void f1(void (__stdcall *)())
{}
void f2(void (__cdecl *)())
{}
void __cdecl h1()
{}
void __stdcall h2()
{}
int main()
{
f1(h1); // error C2664
f2(h2); // error C2664
f1([](){}); // OK
f2([](){}); // OK
auto fn = [](){};
f1(fn); // OK
f2(fn); // OK
}
Creo que los errores son normales, pero las aprobaciones son anormales.
Entonces, mis preguntas son:
¿Cuál es la convención de llamada de una función lambda de C ++?
¿Cómo especificar la convención de llamada de una función lambda de C ++?
Si la convención de llamada no está definida, ¿cómo reciclar correctamente el espacio de pila después de haber llamado a una función lambda?
¿El compilador genera automáticamente múltiples versiones de una función lambda? Es decir, como el siguiente pseudocódigo:
[] __stdcall () {};
[] __cdecl () {}; etc.
En VC ++ 2012, el compilador elige automáticamente la conversión de la llamada para lambdas sin estado (que no tiene variables de captura) al convertir "lambda sin estado en puntero de función".
Características de MSDN C ++ 11 :
Lambdas
[...] Además, en Visual C ++ en Visual Studio 2012, los lambdas sin estado se pueden convertir en punteros de función. [...] (Visual C ++ en Visual Studio 2012 es incluso mejor que eso, porque hemos hecho que los lambdas sin estado se conviertan en punteros de función que tienen convenciones de llamadas arbitrarias. Esto es importante cuando se usan API que esperan cosas como la función
__stdcall
punteros.)
EDITADO:
NB: La conversión de llamada está fuera del estándar de C ++, depende de otras especificaciones, como la plataforma ABI (interfaz binaria de la aplicación).
Las siguientes respuestas se basan en el código de ensamblaje de salida con la opción del compilador / FAs . Así que es una mera conjetura, y pídale a Microsoft más detalles; P
Q1. ¿Cuál es la convención de llamada de una función lambda de C ++?
Q3. Si la convención de llamada no está definida, ¿cómo reciclar correctamente el espacio de pila después de haber llamado a una función lambda?
En primer lugar, C ++ lambda (-expression) NO es una función (ni un puntero de función), puede llamar al operator()
al objeto lambda como una función normal de llamada. Y el código de ensamblaje de salida dice que VC ++ 2012 genera lambda-body con __thiscall
conversión de llamadas.
Q2. ¿Cómo especificar la convención de llamada de una función lambda de C ++?
AFAIK, no hay manera. (Puede ser solo __thiscall
)
Q4. ¿El compilador genera automáticamente múltiples versiones de una función lambda? Es decir, como el siguiente pseudocódigo: [...]
Probablemente No. El tipo lambda VC ++ 2012 proporciona solo una implementación de lambda-body ( void operator()()
), pero proporciona múltiples "conversión definida por el usuario a puntero de función" para cada conversión de llamada (puntero de función de retorno del operador con void (__fastcall*)(void)
, void (__stdcall*)(void)
, y void (__cdecl*)(void)
tipo).
Aquí hay un ejemplo;
// input source code
auto lm = [](){ /*lambda-body*/ };
// reversed C++ code from VC++2012 output assembly code
class lambda_UNIQUE_HASH {
void __thiscall operator()() {
/* lambda-body */
}
// user-defined conversions
typedef void (__fastcall * fp_fastcall_t)();
typedef void (__stdcall * fp_stdcall_t)();
typedef void (__cdecl * fp_cdecl_t)();
operator fp_fastcall_t() { ... }
operator fp_stdcall_t() { ... }
operator fp_cdecl_t() { ... }
};
lambda_UNIQUE_HASH lm;
Una función lambda sin estado sigue siendo una clase, pero una clase que puede convertirse implícitamente en un puntero de función.
El estándar de C ++ no cubre las convenciones de llamada, pero hay pocas razones por las cuales un lambda sin estado no pudo crear un envoltorio en ninguna convención de llamada que se reenvíe al lambda sin estado cuando el lambda se convierte en un puntero de función.
Como ejemplo, podríamos hacer esto:
#include <iostream>
void __cdecl h1() {}
void __stdcall h2(){}
// I''m lazy:
typedef decltype(&h1) cdecl_nullary_ptr;
typedef decltype(&h2) stdcall_nullary_ptr;
template<typename StatelessNullaryFunctor>
struct make_cdecl {
static void __cdecl do_it() {
StatelessNullaryFunctor()();
}
};
template<typename StatelessNullaryFunctor>
struct make_stdcall {
static void __stdcall do_it() {
StatelessNullaryFunctor()();
}
};
struct test {
void operator()() const { hidden_implementation(); }
operator cdecl_nullary_ptr() const {
return &make_cdecl<test>::do_it;
}
operator stdcall_nullary_ptr() const {
return &make_stdcall<test>::do_it;
}
};
donde nuestra clase nula de test
sin estado se puede convertir en un puntero de función cdecl
y stdcall
implícita.
La parte importante de esto es que la convención de llamada forma parte del tipo de puntero de función, por lo que el operator function_type
sabe qué convención de llamada se solicita. Y con un reenvío perfecto, lo anterior puede incluso ser eficiente.