C++ const ref类型的函数参数模板参数不明确

C++ const ref类型的函数参数模板参数不明确,c++,templates,c++11,forwarding-reference,C++,Templates,C++11,Forwarding Reference,将const ref参数传递给调用其他函数的模板函数时出现问题。考虑下面的代码: struct A { void foo(const int& i) { } }; template <class ...Args> void a_caller(A& a, void(A::*f)(Args...), Args&& ...args) { (a.*f)(std::forward<Args>(args)...); } int m

将const ref参数传递给调用其他函数的模板函数时出现问题。考虑下面的代码:

struct A
{
    void foo(const int& i) { }
};

template <class ...Args> 
void a_caller(A& a, void(A::*f)(Args...), Args&& ...args)
{
    (a.*f)(std::forward<Args>(args)...);
}

int main()
{
    int i = 42;
    A a;

    a_caller(a, &A::foo, i); // (1) compiler error
    a_caller<const int&>(a, &A::foo, i); // (2) ok
}
我的第一个问题是为什么会发生这种情况?我给编译器一个非重载函数a::foo,为什么它不能从中推断出
Args
? 第二个问题是为什么std::make_不具有唯一性?以下代码在我看来是相同的,但编译器在推导构造函数参数类型时没有问题:

struct A
{
    A(const int& i)  { }
};

int main()
{
    int i = 42;
    auto aptr = std::make_unique<A>(i);
}
结构A { A(常数int&i){} }; int main() { int i=42; 自动aptr=std::使_唯一(i); }
您试图迫使
Args
履行两个不同(但不一定兼容)的角色。第一个角色是
f
的参数类型。第二种是提供给调用方的参数类型

由于完美转发的实现方式,在您的示例中传递
i
希望推断此
i
Args
类型为
int&
。但是,在
A::foo
中相同的
Args
类型属于
const int&
——因此推断不明确

在某种程度上,完美转发的关键在于,转发参数的类型是当场推导出来的(并且通常不可用于其他任何事情)。所以你必须这样做:

template <class ...Params, class ...Args>
void a_caller(A& a, void(A::*f)(Params...), Args&& ...args)
{
  (a.*f)(std::forward<Args>(args)...);
}
#include <memory>

struct A
{
    void foo(const int& i) { }
};

template <typename F, class ...Args> 
void a_caller(A& a, F &&f, Args&& ...args)
{
    (a.*f)(std::forward<Args>(args)...);
}

int main()
{
    int i = 42;
    A a;

    a_caller(a, &A::foo, i);
}
模板
void a_调用者(a&a,void(a::*f)(参数…,参数和参数)
{
(a.*f)(标准:转发(args)…);
}

当参数与参数不匹配时,您必须依靠调用
f
来通知您。

错误消息告诉您发生了什么

see declaration of 'a_caller'
could be 'const int&'
or       'int&'
因此,您正在传递接受
const int&
的成员函数,因此编译器将
Args
推断为
const int&
,但您也将
i
传递给
Args
,并将其推断为
int&
。这些冲突会导致错误。您可以
i
编译,也可以传递
const int
作为第二个参数

a_caller(a, &A::foo, const_cast<const int&>(i)); 
const int foo = 42;
a_caller(a, &A::foo, foo);
a_调用者(a,&a::foo,const_cast(i));
常数int foo=42;
a_调用者(a,&a::foo,foo);
我的第一个问题是为什么会发生这种情况?我给编译器一个非重载函数a::foo,为什么它不能从中推断Args

因为您尝试两次推导Args,用于函数a_调用者的第一个和第二个参数。这个推导出的类型不匹配,第一个参数为
const int&
,第二个参数为
int&

第二个问题是为什么std::make_不具有唯一性

因为make_unique只是将其参数转发给类构造函数

我认为您的代码应该如下所示:

template <class ...Params, class ...Args>
void a_caller(A& a, void(A::*f)(Params...), Args&& ...args)
{
  (a.*f)(std::forward<Args>(args)...);
}
#include <memory>

struct A
{
    void foo(const int& i) { }
};

template <typename F, class ...Args> 
void a_caller(A& a, F &&f, Args&& ...args)
{
    (a.*f)(std::forward<Args>(args)...);
}

int main()
{
    int i = 42;
    A a;

    a_caller(a, &A::foo, i);
}
#包括
结构A
{
void foo(const int&i){}
};
模板
无效a_调用方(a&a、F&F、Args&&…Args)
{

(a.*f)(std::forward

感谢使用方法指针作为模板参数的提示-看起来比Angew方案短(对于方法返回类型不重要的情况)