const ref 类型的函数参数模板参数不明确
Function argument template parameter ambiguous for const ref types
我在将 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
,我想在包装器 a_caller
中调用带有 const int&
参数的成员函数。第 (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);
}
您正试图强行 Args
履行两个截然不同(不一定兼容)的角色。第一个作用是f
的参数类型。第二个是给 a_caller
.
的参数类型
由于实现完美转发的方式,在您的示例中传递 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)...);
}
您将不得不依靠 f
的调用来告诉您参数何时与参数不匹配。
错误消息告诉您发生了什么
see declaration of 'a_caller'
could be 'const int&'
or 'int&'
所以你传递的成员函数采用 const int&
所以编译器推断 Args
作为 const 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);
我的第一个问题是为什么会这样?我给编译器一个非重载函数 A::foo,为什么它不能从中推导出 Args?
因为您尝试为函数 a_caller 的第一个和第二个参数推导两次 Args。并且此推导类型不匹配,第一个参数为 const int&
,第二个参数为 int&
。
第二个问题是为什么 std::make_unique 没有发生这种情况?
因为 make_unique 只是将其参数转发给 class 构造函数。
我认为您的代码应该如下所示:
#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);
}
我在将 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
,我想在包装器 a_caller
中调用带有 const int&
参数的成员函数。第 (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);
}
您正试图强行 Args
履行两个截然不同(不一定兼容)的角色。第一个作用是f
的参数类型。第二个是给 a_caller
.
由于实现完美转发的方式,在您的示例中传递 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)...);
}
您将不得不依靠 f
的调用来告诉您参数何时与参数不匹配。
错误消息告诉您发生了什么
see declaration of 'a_caller'
could be 'const int&'
or 'int&'
所以你传递的成员函数采用 const int&
所以编译器推断 Args
作为 const 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);
我的第一个问题是为什么会这样?我给编译器一个非重载函数 A::foo,为什么它不能从中推导出 Args?
因为您尝试为函数 a_caller 的第一个和第二个参数推导两次 Args。并且此推导类型不匹配,第一个参数为 const int&
,第二个参数为 int&
。
第二个问题是为什么 std::make_unique 没有发生这种情况?
因为 make_unique 只是将其参数转发给 class 构造函数。
我认为您的代码应该如下所示:
#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);
}