变长模板参数包在推导时不一致

9
我有一个C++11的例子,其中我有一个名为call的函数,它使用可变参数模板来接受并调用通用类方法。
#include <utility>

template <typename T, typename R, typename... Args>
R call(R (T::*fn)(Args...), T *t, Args&&... args) {
    return ((*t).*fn)(std::forward<Args>(args)...);    
}

class Calculator {
    public:
    int add(const int& a, const int& b) {
        return a + b;
    }
};

int main() {
    Calculator *calculator = new Calculator();
    int* a = new int(2);    
    int* b = new int(4);

    // compiles
    int res1 = calculator->add(*a, *b);    

    // does not compile!
    int res2 = call<Calculator>(&Calculator::add,calculator, *a, *b);    

    return 0;
}

如代码中所述,当函数接受 const int 时,我无法传递 int,但在类似于方向方法调用的情况下,我可以。 我收到以下编译错误:

error: no matching function for call to ‘call(int (Calculator::*)(const int&, const int&), Calculator*&, int&, int&)’
     int res2 = call<Calculator>(&Calculator::add,calculator, *a, *b);    
                                                                    ^

inconsistent parameter pack deduction with ‘const int&’ and ‘int&’
     int res2 = call<Calculator>(&Calculator::add,calculator, *a, *b);    
                                                                    ^

使用C++变长模板会比普通执行更加强制进行类型检查吗?我正在使用带有C++ 11的g++ 4.8.1。


也许你会更喜欢Clang的错误提示:注意:候选模板被忽略:参数'Args'的推断类型冲突(<const int&,const int&>与<int&,int&>) - chris
那么使用可变参数模板时,类型必须完全匹配吗? - jeffreyveon
3
这意味着您不能为同一模板参数推断出不同的类型,因为编译器无法知道您想要哪个类型。您可以始终跳过明确的函数捕获并只使用可调用对象参数:template <typename F, typename T, typename... Args> auto call(F f, T *t, Args&&... args) { return (t->*f)(std::forward<Args>(args)...); } - chris
@chris:采用这种方法,我的调用方代码应该是什么样子的?对于类型 F,我应该传递什么参数? - jeffreyveon
只需删除显式模板参数即可。 - chris
1个回答

16
您调用函数模板的方式,模板参数包Args将从以下两个来源中推断出:

  • 成员函数指针的类型 - int (Calculator::*)(const int&, const int&)
  • 传递给函数参数包*a, *b的实际类型 - int &, int &

为了使推断成功,推断结果必须完全匹配。但它们显然不匹配。

这并不是可变参数模板特有的新问题。如果您尝试执行std::max(1, 1.5),也会遇到相同的问题-编译器从一个参数中推断出int,从另一个参数中推断出double,推断失败因为两者冲突。

最简单的解决方法可能是使用两个参数包:

template <typename T, typename R, typename... Args1, typename... Args2>
R call(R (T::*fn)(Args1...), T *t, Args2&&... args) {
    return ((*t).*fn)(std::forward<Args2>(args)...);    
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接