c++ - ¿Hay alguna diferencia entre std:: forward<T> y std:: forward<decltype(t)>?
c++11 perfect-forwarding (2)
¿Son estas funciones dos equivalentes?
Sí , son equivalentes. decltype(t)
es lo mismo que T&&
, y cuando se usa con std::forward
, no hay diferencia entre T
y T&&
, independientemente de que T
sea.
¿Puedo usar siempre esta macro para un reenvío perfecto?
Sí tu puedes. Si desea que su código sea ilegible y no se pueda mantener, hágalo. Pero te aconsejo fuertemente que no lo hagas. Por un lado, básicamente no ganas nada al usar esta macro. Y, por otro lado, otros desarrolladores tienen que echar un vistazo a la definición para entenderla, y puede resultar en errores sutiles. Por ejemplo, agregar paréntesis adicionales no funcionará:
MY_FORWARD((t))
En contraste, la forma con decltype
es perfectamente válida. En particular, es la forma preferida de reenviar parámetros desde expresiones lambda genéricas, porque no hay parámetros de tipo explícitos:
[](auto&& t) { foobar(std::forward<decltype(t)>(t)); }
Ignoré la tercera variante con std::forward(t)
, porque no es válida.
Actualización: con respecto a su ejemplo: puede usar llamada por valor en lugar de llamada por referencia para la plantilla de función foo
. Luego puedes usar std::move
lugar de std::forward
. Esto agrega dos movimientos adicionales al código, pero no operaciones de copia adicionales. Por otro lado, el código se vuelve mucho más limpio:
template <class T, class U>
auto foo(T func, U para)
{
auto val = // some calculation
return [func=std::move(func),para=std::move(para),val=std::move(val)] {
// some code use val
func(std::move(para));
};
}
¿Son estas funciones equivalentes?
template <class T>
void foo(T && t)
{
bar(std::forward<T>(t));
}
template <class T>
void foo2(T && t)
{
bar(std::forward<decltype(t)>(t));
}
template <class T>
void foo3(T && t)
{
bar(std::forward(t));
}
Si lo son, ¿puedo usar siempre esta macro para un reenvío perfecto?
#define MY_FORWARD(var) std::forward<decltype(var)>(var)
o simplemente usar
bar(std::forward(t));
Creo que foo2
y foo3
son iguales, pero descubrí que las personas siempre se usan hacia adelante como foo
, ¿hay alguna razón para escribir explícitamente el tipo?
Entiendo que T
y T&&
son dos tipos diferentes, pero creo que std::forward<T>
y std::forward<T&&>
siempre dan el mismo resultado?
Editar:
la razón por la que quiero usar macro es que quiero guardar algo de escritura en el siguiente código C ++ 1y, tengo muchos códigos similares en diferentes lugares
#define XLC_FORWARD_CAPTURE(var) var(std::forward<decltype(var)>(var))
#define XLC_MOVE_CAPTURE(var) var(std::move(var))
template <class T, class U>
auto foo(T && func, U && para )
{
auto val = // some calculation
return [XLC_FORWARD_CAPTURE(func),
XLC_FORWARD_CAPTURE(para),
XLC_MOVE_CAPTURE(val)](){
// some code use val
func(std::forward<U>(para));
};
}
La respuesta aceptada no resuelve el problema en el título por completo.
Un argumento macro conserva el tipo de expresión. Un parámetro de reenvío en una plantilla no lo hace. Esto significa que t
en foo2
(como parámetro de la función de reenvío) tiene el tipo T&&
(porque este es el parámetro de la plantilla de reenvío), pero puede ser algo diferente cuando la macro está en otros contextos. Por ejemplo:
using T = int;
T a = 42;
T&& t(std::move(a));
foo(MY_FORWARD(t)); // Which foo is instantiated?
Tenga en cuenta que t
no es un xvalue, sino un lvalue . Con std::forward<T>(t)
, que es equivalente a std::forward<int>(t)
, t
se reenviaría como un valor l. Sin embargo, con MY_FORWARD(t)
, que es equivalente a std::forward<int&&>(t)
, t
se reenviaría como un valor x. Esta diferencia dependiente del contexto se desea en algún momento cuando tiene que lidiar con algunas variables declaradas con tipos de referencia de valor (no es posible que el parámetro de reenvío se vea similar en la sintaxis).