c++ - ¿Por qué el argumento de la plantilla no se puede deducir en este contexto?
c++11 templates (2)
Resumiendo la discusión en los comentarios: el soporte para funciones de miembro calificadas de referencia como argumentos de plantilla es una característica relativamente nueva para algunos compiladores. Sin embargo, las últimas versiones de la mayoría de los compiladores compilarán dicho código.
Por ejemplo:
#include <iostream>
struct MyClass
{
void Foo(int) const &
{
std::cout << "calling: void Foo(int) const &/n";
}
void Foo(int) const &&
{
std::cout << "calling: void Foo(int) const &&/n";
}
};
template<typename T>
void CallFoo_lvalue(void (T::*foo)(int) const &)
{
T temp;
(temp.*foo)(0);
}
template<typename T>
void CallFoo_rvalue(void (T::*foo)(int) const &&)
{
(T{}.*foo)(0);
}
int main()
{
CallFoo_lvalue(&MyClass::Foo);
CallFoo_rvalue(&MyClass::Foo);
}
Se compilará con:
- gcc (funciona desde 7.0.0)
- Visual C ++ (funciona con v19.10.24903.0)
produciendo la siguiente salida:
calling: void Foo(int) const &
calling: void Foo(int) const &&
Para aquellos que se preguntan para qué son &&&&&: aquí está la cita de @JustinTime:
Básicamente, & es el calificador ref de lvalue, y && es el calificador ref de rvalue (se une al objeto temporal); en su ejemplo, MyClass m; m.Foo (3); llamaría el superior, mientras que MyClass {}. Foo (3); llamaría el de abajo. Actúan sobre el parámetro objeto implícito; lvalue ref-qualifier se enlaza a lvalue reference, y rvalue ref-qualifier se enlaza a rvalue reference (las funciones que no tienen el parámetro como lvalue reference, sino que lo permiten). Tenga en cuenta que en realidad no cambian * este es el tipo.
¿Alguien podría explicar por qué los compiladores (g ++, visual c ++) no pueden deducir el argumento de la plantilla en este caso?
struct MyClass
{
void Foo(int x)& {}
void Foo(int x)&& {}
};
template<typename T>
void CallFoo(void(T::*func)(int)&)
{
//create instance and call func
}
int main()
{
CallFoo(&MyClass::Foo); // Fails to deduce T
}
¿Por qué los compiladores no pueden deducir T a MyClass? Esto sucede solo para los métodos sobrecargados por ref calificadores. Si un método está sobrecargado por constancia o tipos de parámetros, todo funciona bien. Parece que solo Clang puede deducir T en este caso.
Si desea que su plantilla se enlace a diferentes tipos de referencia, necesita usar la referencia universal
template<typename T>
void func(T&& arg)
{
other_func(std::forward<T>(arg));
}
Eso se unirá a las referencias lvalue o rvalue. std :: forward se asegurará de que se use la referencia apropiada en las llamadas subsiguientes. No estoy seguro de cómo encajar el doble ampersand en su código, pero tal vez solo
template<typename T>
void CallFoo(void(T::*func)(int)&&)
Tal vez mejor seria
template<typename func_t>
void CallFoo(func_t && f)
{
call(std::forward<func_t>(f));
}
template<typename func_t>
void call(typename std::remove_reference<func_t> & f)
{
f();
}
template<typename func_t>
void call(typename std::remove_reference<func_t> && f)
{
f();
}
o cualquier sintaxis que necesite para invocar un puntero de función, tal vez * f ();
Y si quieres pasar argumentos también:
template<typename func_t, typename ... args_t>
void CallFoo(func_t && f, args_t && ... args)
{
call(std::forward<func_t>(f), std::forward<args_t>(args)...);
}
template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> & f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}
template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> && f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}