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


14

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

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);
}

这段代码非常适合class A如下所示:

class A {
public:
    void foo(int x) {

    }
};

但无法像这样编译:

class A {
public:
    void foo(const int& x) {

    }
};

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


也许Args&&...std::forward
fas

@ user3365922尝试过。感觉像解决方案,不起作用
bartop,

不会可以帮助您在正确的方向?
Gizmo

Answers:


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());
    }
}

关于成员函数的简历资格的要点,我认为这是迄今为止最好的解决方案
bartop

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);

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


1
您可能想要Args&&... args
Jarod42

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

生活

PS:std::type_identity从C ++ 20开始受支持;但实施它很容易。


1
它会以某种方式与完美转发一起工作吗?
bartop

@bartop我这么认为。我们可以使第4个参数符合转发参考样式,即Args&&...,然后将std::type_identity第3个参数放在上R (T::*fun)(std::type_identity_t<Args>...)LIVELIVE
songyuanyao

@songyuanyo是的,但是这样会破坏价值论点。
bartop

您已经可以从代码Demo中使用forward 。它只会做“额外”动作。
Jarod42
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.