c++ - La función de plantilla no funciona para la función de puntero a miembro tomando const ref
templates (3)
Últimamente escribí una función de plantilla para resolver algunas repeticiones de código. Se parece a esto:
template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
if (auto sp = ptr.lock())
{
return std::invoke(fun, *sp, args...);
}
else
{
throw std::runtime_error(error.c_str());
}
}
int main() {
auto a = std::make_shared<A>();
call_or_throw(std::weak_ptr<A>(a), "err", &A::foo, 1);
}
Este código funciona perfectamente para la
class A
que se ve así:
class A {
public:
void foo(int x) {
}
};
Pero no puede compilar para uno como este:
class A {
public:
void foo(const int& x) {
}
};
¿Por qué es así (por qué quiero decir por qué no puede deducir el tipo) y cómo (si es posible) puedo hacer que este código funcione con referencias? Ejemplo en vivo
Su problema es que tiene deducciones por conflicto para
Args
entre:
-
R (T::*fun)(Args...)
-
Args... args
Sugiero tener más código genérico (sin duplicaciones entre
R (T::*fun)(Args...)
y
const versión
R (T::*fun)(Args...) const
y otra alternativa) con:
template<class T, class F, class... Args>
decltype(auto) call_or_throw(const std::weak_ptr<T>& ptr,
const std::string& error,
F f,
Args&&... args)
{
if (auto sp = ptr.lock())
{
return std::invoke(f, *sp, std::forward<Args>(args)...);
}
else
{
throw std::runtime_error(error.c_str());
}
}
Tenga en cuenta que el tipo de
Args
parámetro de plantilla se deduce como
const int&
en el argumento de la 3ª función
&A::foo
, y se deduce como
int
en el 4 ° parámetro de la función
1
.
No coinciden y hacen que la deducción falle.
Puede excluir el cuarto parámetro de la deduction , p. Ej.
template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr,
const std::string& error,
R (T::*fun)(Args...),
std::type_identity_t<Args>... args) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
PS:
std::type_identity
es compatible desde C ++ 20;
pero es bastante fácil implementar uno.
Args
tipos
Args
no pueden deducirse tanto
const&
(de la declaración de parámetros
fun
) como sin referencia de la declaración
args
.
Una solución simple es usar dos paquetes de parámetros de tipo de plantilla separados:
template<class T, class R, class... Args, class... DeclaredArgs>
R call_or_throw(
const std::weak_ptr<T>& ptr,
const std::string& error,
R (T::*fun)(DeclaredArgs...),
Args... args);
Como inconveniente, puedo imaginar mensajes de error un poco más largos en caso de mal uso.