最近,我编写了一个模板函数来解决一些代码重复。看起来像这样:
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);
}
Run Code Online (Sandbox Code Playgroud)
这段代码非常适合class A如下所示:
class A {
public:
void foo(int x) {
}
};
Run Code Online (Sandbox Code Playgroud)
但无法像这样编译:
class A {
public:
void foo(const int& x) {
}
};
Run Code Online (Sandbox Code Playgroud)
为什么会这样(为什么我要说为什么它无法推断类型)以及如何(如果可能的话)使此代码与引用一起使用? 现场例子
Args类型既不能推论为const&(根据fun参数声明),也不能推导为非引用args。一个简单的解决方法是使用两个单独的模板类型参数包:
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);
Run Code Online (Sandbox Code Playgroud)
缺点是,如果使用不当,我可以想象出更长的错误消息。
请注意,模板参数Args的类型const int&由第3个函数参数&A::foo推导,并由int第4个函数参数推导1。他们不匹配,导致推论失败。
您可以从推论中排除第4个参数,例如
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) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
PS:std::type_identity从C ++ 20开始受支持;但实施它很容易。
您的问题是您在Args以下方面有冲突扣除:
R (T::*fun)(Args...)Args... argsR (T::*fun)(Args...)我建议使用更通用的代码( const
版本和其他替代方案之间没有重复R (T::*fun)(Args...) const):
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());
}
}
Run Code Online (Sandbox Code Playgroud)