【问题标题】:Template function does not work for pointer-to-member-function taking const ref模板函数不适用于采用 const ref 的指向成员函数的指针
【发布时间】:2019-10-23 10:37:57
【问题描述】:

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

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

    }
};

为什么会这样(我的意思是为什么它无法推断出类型)以及如何(如果可能的话)使这段代码与引用一起工作? Live example

【问题讨论】:

  • 可能是Args&amp;&amp;...std::forward
  • @user3365922 试过了。感觉像解决方案,不起作用
  • thisthis 不会帮助您朝着正确的方向前进吗?

标签: c++ templates


【解决方案1】:

Args 类型不能同时推断为const&amp;(来自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);

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

【讨论】:

  • 你可能想要Args&amp;&amp;... args
【解决方案2】:

注意,模板参数Args的类型在第三个函数参数&amp;A::foo上推导出为const int&amp;,在第四个函数参数1上推导出为int。不匹配导致扣减失败。

您可以从deduction 中排除第四个参数,例如

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:自 C++20 起支持std::type_identity;但它很容易实现。

【讨论】:

  • @bartop 我想是的。我们可以使第4个参数符合转发引用样式,即Args&amp;&amp;...,然后将std::type_identity放在第3个参数上,如R (T::*fun)(std::type_identity_t&lt;Args&gt;...)LIVELIVE
  • @songyuanyo 是的,但是它会因为价值论点而中断。
  • 您已经可以从您的代码Demo 中使用forward。它只会做“额外”的动作。
【解决方案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());
    }
}

【讨论】:

  • 关于成员函数的 cv 限定的好点,我认为这是迄今为止最好的解决方案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 2010-09-13
  • 2011-03-04
  • 2020-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多