模板函数不适用于采用const ref的指针到成员函数

bar*_*top 14 c++ templates

最近,我编写了一个模板函数来解决一些代码重复。看起来像这样:

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)

为什么会这样(为什么我要说为什么它无法推断类型)以及如何(如果可能的话)使此代码与引用一起使用? 现场例子

Log*_*uff 8

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)

缺点是,如果使用不当,我可以想象出更长的错误消息。


son*_*yao 5

请注意,模板参数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开始受支持;但实施它很容易。


Jar*_*d42 3

您的问题是您在Args以下方面有冲突扣除:

  • R (T::*fun)(Args...)
  • Args... args

R (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)