3

我在将 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带有const int&参数的成员函数,我想在 wrapper 中调用它a_caller。第 (1) 行导致以下错误:

'void a_caller(A &,void (__thiscall A::* )(Args...),Args &&...)' : template parameter 'Args' is ambiguous
see declaration of 'a_caller'
could be 'const int&'
or       'int&'

我的第一个问题是为什么会这样?我给编译器一个非重载函数A::foo,为什么不能从中推导Args出来?第二个问题是为什么 std::make_unique 不会发生这种情况?以下代码对我来说看起来相同,但编译器推断构造函数参数类型没有问题:

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

int main()
{
    int i = 42;
    auto aptr = std::make_unique<A>(i);
}
4

3 回答 3

7

您正试图强行Args完成两个不同(不一定兼容)的角色。第一个作用是参数的类型f。第二个是给定的参数类型a_caller

由于实现了完美转发的方式,i在您的示例中传递 as 想要将其Args类型推断iint &. 然而,相同的Args类型A::foo是类型const int &——因此是模棱两可的推论。

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

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

f当参数与参数不匹配时,您将不得不依靠调用来告诉您。

于 2015-08-17T12:41:37.357 回答
1

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

see declaration of 'a_caller'
could be 'const int&'
or       'int&'

因此,您传递的成员函数采用 a const int&,因此编译器将其推断Args为 aconst int&但您也传递iArgs它推断为a 的成员函数int&。这些冲突让你得到一个错误。你可以编译,或者你可以传递 a作为第二个参数const_cast iconst int

a_caller(a, &A::foo, const_cast<const int&>(i)); 
const int foo = 42;
a_caller(a, &A::foo, foo);
于 2015-08-17T12:42:43.603 回答
1

我的第一个问题是为什么会这样?我给编译器一个非重载函数A::foo,为什么它不能从中推导出Args?

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

第二个问题是为什么 std::make_unique 不会发生这种情况?

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

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

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

演示

于 2015-08-17T12:58:41.147 回答