临时值的 C++ 模板类型推导
C++ Template type deduction for temporary value
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void wrapper(T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error
}
您好,我有一个问题,为什么最后一条语句给出编译器错误。
:18:10: error: cannot bind non-const lvalue reference of type
'A&' to an rvalue of type 'A' wrapper(A());
我认为模板类型 T
会被推断为 const A&
因为 ca
也被推断为 const A&
。为什么在这种情况下类型推导会失败?
I thought that the template type T would be deduced as const A& as the ca is also deduced as const A&. Why the type deduction fails in this case?
因为扣除规则不是这样运作的。他们努力推断出尽可能多的与函数参数类型的匹配。临时对象不一定是 const,它可以绑定到 const 引用。
但是您的函数模板不接受 const 引用,而是接受 non-const 左值引用。所以没有 const
会 spring 除非函数参数是 const 本身(它不是)。
正确的解决方案是使用转发引用(对推导的模板参数的右值引用):
template <typename T>
void wrapper(T&& u)
{
g(std::forward<T>(u));
}
现在u
可以绑定到任何对象。推导的类型会告诉您函数参数的值类别,允许您将该类别转发给函数调用 g(...)
,并确保选择了正确的重载。
顺便说一句,如果你很好奇,如果你直接将 const 添加到临时类型,你的原始代码将构建得很好。
using CA = A const;
wrapper(CA()); // Not an error anymore
现在 u
最终成为 A const&
并且绑定到临时文件就好了。但这只是一种好奇心,在实践中不太可能有用。使用转发引用。
Why the type deduction fails in this case?
它没有失败。 T
推导为 A
。因此,参数u
的类型是A&
。
对 wrapper()
的调用实际上失败了,因为 rvalue(即:A()
在这种情况下)不能绑定到非const
左值引用(即:参数u
)。
错误消息试图传达:
非const
引用无法绑定到临时值,因为临时对象的生命周期在控件到达函数之前就已经过期。
- 变量
ca
不是临时变量,因为它有一个可以在main
. 中引用的名字
- 它的生命周期不会在
main
结束之前到期,因此可以在 wrapper
. 中安全地引用它
- 模板类型推导不会删除
const
,因为它会违反 const
-ca
的正确性。
对于 wrapper(A());
,类型参数 T
将推导为 A
。由于临时没有 const
-ness,因此参数 u
将被推断为 A&
,但由于非 const
引用无法绑定到临时值由于上述原因,没有 wrapper
函数可以使用参数 A()
.
来调用
现在,C++ 具有延长临时对象生命周期的功能,如果您只是从中读取的话。这就是 const T&
绑定到临时对象的原因。在你的例子中,无名临时对象的生命周期被延长到 wrapper
函数结束。
如果您将类型参数显式设置为 const T&
,则如下:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error no more.
}
编译得很好。
[C++11]
对于wrapper(A());
,类型参数T
仍然会推导为A
,而参数u
的类型为A&&
,称为对 A
的右值引用。
假设我们向 wrapper
添加另一个重载,以便如下:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
template <typename T>
void wrapper(T&& u)
{
g(u);//u is an lvalue since it has a name.
}
存在。
您可以预期 wrapper(A());
可以编译,更喜欢右值重载,因为右值引用是 wrapper
中的左值,因为它有一个名称。
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void wrapper(T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error
}
您好,我有一个问题,为什么最后一条语句给出编译器错误。
:18:10: error: cannot bind non-const lvalue reference of type 'A&' to an rvalue of type 'A' wrapper(A());
我认为模板类型 T
会被推断为 const A&
因为 ca
也被推断为 const A&
。为什么在这种情况下类型推导会失败?
I thought that the template type T would be deduced as const A& as the ca is also deduced as const A&. Why the type deduction fails in this case?
因为扣除规则不是这样运作的。他们努力推断出尽可能多的与函数参数类型的匹配。临时对象不一定是 const,它可以绑定到 const 引用。
但是您的函数模板不接受 const 引用,而是接受 non-const 左值引用。所以没有 const
会 spring 除非函数参数是 const 本身(它不是)。
正确的解决方案是使用转发引用(对推导的模板参数的右值引用):
template <typename T>
void wrapper(T&& u)
{
g(std::forward<T>(u));
}
现在u
可以绑定到任何对象。推导的类型会告诉您函数参数的值类别,允许您将该类别转发给函数调用 g(...)
,并确保选择了正确的重载。
顺便说一句,如果你很好奇,如果你直接将 const 添加到临时类型,你的原始代码将构建得很好。
using CA = A const;
wrapper(CA()); // Not an error anymore
现在 u
最终成为 A const&
并且绑定到临时文件就好了。但这只是一种好奇心,在实践中不太可能有用。使用转发引用。
Why the type deduction fails in this case?
它没有失败。 T
推导为 A
。因此,参数u
的类型是A&
。
对 wrapper()
的调用实际上失败了,因为 rvalue(即:A()
在这种情况下)不能绑定到非const
左值引用(即:参数u
)。
错误消息试图传达:
非const
引用无法绑定到临时值,因为临时对象的生命周期在控件到达函数之前就已经过期。
- 变量
ca
不是临时变量,因为它有一个可以在main
. 中引用的名字
- 它的生命周期不会在
main
结束之前到期,因此可以在wrapper
. 中安全地引用它
- 模板类型推导不会删除
const
,因为它会违反const
-ca
的正确性。
对于 wrapper(A());
,类型参数 T
将推导为 A
。由于临时没有 const
-ness,因此参数 u
将被推断为 A&
,但由于非 const
引用无法绑定到临时值由于上述原因,没有 wrapper
函数可以使用参数 A()
.
现在,C++ 具有延长临时对象生命周期的功能,如果您只是从中读取的话。这就是 const T&
绑定到临时对象的原因。在你的例子中,无名临时对象的生命周期被延长到 wrapper
函数结束。
如果您将类型参数显式设置为 const T&
,则如下:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error no more.
}
编译得很好。
[C++11]
对于wrapper(A());
,类型参数T
仍然会推导为A
,而参数u
的类型为A&&
,称为对 A
的右值引用。
假设我们向 wrapper
添加另一个重载,以便如下:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
template <typename T>
void wrapper(T&& u)
{
g(u);//u is an lvalue since it has a name.
}
存在。
您可以预期 wrapper(A());
可以编译,更喜欢右值重载,因为右值引用是 wrapper
中的左值,因为它有一个名称。