在带有模板的结构中,为啥左值会推导出为右值?
Posted
技术标签:
【中文标题】在带有模板的结构中,为啥左值会推导出为右值?【英文标题】:In a struct with a template, why does an lvalue deduce as an rvalue?在带有模板的结构中,为什么左值会推导出为右值? 【发布时间】:2018-06-22 03:41:22 【问题描述】:当我使用long
变量调用g()
函数时,它为什么调用g(const T& x)
?
如果我将long
更改为int
,它会改为调用g(T& x)
。
#include <iostream>
template <typename T>
struct s
void g(const T& x) std::cout << "1\n";
void g(T& x) std::cout << "2\n";
;
int main()
s<int> x;
long y = 0;
x.g(y);
结果:
1
【问题讨论】:
你没有用左值调用它。 在调用站点可能存在从long
到int
的隐式转换,因此传递的是右值。
【参考方案1】:
非常量 int&
引用只能绑定到 int
变量,不能绑定其他任何东西。 long
和 int
是独立且不同的类型,因此不能将 long
变量分配给非常量 int&
引用。只有当y
是int
时,g(y)
才能调用g(int&)
。
定义了从long
到int
的隐式转换,并且const 引用可以绑定到临时变量。当y
为long
时,g(y)
无法调用g(int&)
,但编译器可以从long
值创建一个临时的int
变量来调用g(const int&)
。
【讨论】:
【参考方案2】:简化您的代码以清楚地查看错误:
void g(int& x) ...
long y = 0;
g(y);
这将导致错误
无法将“int&”类型的非常量左值引用绑定到 输入'int'
为什么?
非常量左值引用(即int&
)将仅绑定到相同类型的非常量左值。
只有const
左值引用(即const int&
)可以绑定到右值(这是一个由long
隐式转换为@987654326而产生的临时对象@)。
因此,
void g(const T& x) std::cout << "1\n";
将在您给定的代码中通过重载决议进行选择。
【讨论】:
【参考方案3】:函数调用检查重载函数的最近匹配。
它使用任何现有的非常量函数调用检查输入参数的类型 - 在本例中为 long。 g(int&) 不匹配 long,因此被丢弃。
接下来它检查第二个函数 g(const int&)。这也不匹配。
它通过向下转换输入参数来检查它是否可以匹配其中任何一个。将 y 从 long 向下转换为 int。由于这种向下转换会产生一个右值,并且只有 const 左值引用可以绑定到右值,所以调用 g(const int&)。
【讨论】:
【参考方案4】:将模板类型指定为int
、T = int
后,您的函数将变为void g(const int& x)
和void g(int& x)
。接下来,编译器将执行模板类型推导。由于您传入long
,编译器将尝试推断哪个函数适合long
“最佳”。
由于long
显然不是int&
并且无法将long
转换为int&
,因此编译器“丢弃”了该函数,只剩下函数g(const int& x)
。但是,long
可以转换为 int
,这是编译器所做的,因为它是从 long
到 int
的隐式转换,它是一个 r 值,因此函数 @987654336 @ 被调用。
【讨论】:
【参考方案5】:我认为@codekaizer 和@RemyLebeau 给出了正确的答案。您只能将右值绑定到 const 左值引用。我做了一个测试。
#include "print_type.h"
#include <iostream>
template<typename T> struct s
void foo(const T& t) std::cout << "calling foo(const " << type_name<T>() << "&)\n";
void foo(T& t) std::cout << "calling foo(" << type_name<T>() << "&)\n";
;
template<typename T>
void bar(const T& t) std::cout << "calling bar(const " << type_name<T>() << "&)\n";
template<typename T>
void bar(T& t) std::cout << "calling bar(" << type_name<T>() << "&)\n";
void g(int& i) std::cout << "calling g(int&)\n";
void g(const int& i) std::cout << "calling g(const int&)\n";
int main()
s<int> x;
int i = 0;
x.foo(i);
bar(i);
const int ci = 0;
x.foo(ci);
bar(ci);
long l = 0;
x.foo(l);
bar(l);
g(l);
return 0;
上面的代码sn -p输出如下结果:
calling foo(int&)
calling bar(int&)
calling foo(const int&)
calling bar(const int&)
calling foo(const int&)
calling bar(long&)
calling g(const int&)
如果我们不定义函数void g(const int&)
,就会报错candidate function not viable: no known conversion from 'long' to 'int &' for 1st argument
。
【讨论】:
以上是关于在带有模板的结构中,为啥左值会推导出为右值?的主要内容,如果未能解决你的问题,请参考以下文章
error C2662 无法将左值绑定到右值 —— 变量永远是左值,即使它的类型为右值引用
C++11新特性:21—— C++11 move()函数:将左值强制转换为右值
C++ std::move()函数(将一个左值强制转化为右值引用,继而可以通过右值引用使用该值)