c++ templates

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) { // ^^^^^^^^^^^^^^^^^^^^^^^^^^

LIVE

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.