对于const引用类型,函数参数模板参数含义不明确。

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&参数,我想在包装器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);
}
3个回答

7
您正在试图强行让Args扮演两个不同(并且不一定兼容)的角色。第一个角色是f参数的类型。第二个角色是传递给a_caller的参数类型。
由于完美转发的实现方式,像您示例中传递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)...);
}

你必须依赖于f的调用,它会告诉你参数与形参不匹配。


1
错误信息告诉您发生了什么。
see declaration of 'a_caller'
could be 'const int&'
or       'int&'

所以你正在传递一个接受const int&的成员函数,因此编译器推断Argsconst int&,但是你还传递了i作为Args,它被推断为int&。这些冲突导致错误。你可以使用const_cast i来编译,或者将一个const int作为第二个参数传递。

a_caller(a, &A::foo, const_cast<const int&>(i)); 
const int foo = 42;
a_caller(a, &A::foo, foo);

1
我的第一个问题是为什么会发生这种情况?我给编译器一个非重载函数A :: foo,为什么它不能从中推断出Args?
因为您尝试对a_caller函数的第一个和第二个参数进行两次推断。而这些推断出的类型不匹配,第一个参数为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);
}

演示


谢谢您提供的方法指针作为模板参数的提示 - 看起来比Angew的建议更简短(对于方法的返回类型不重要的情况)。 - Lao

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