在带有模板的结构中,为什么左值推导为右值?
In a struct with a template, why does an lvalue deduce as an rvalue?
当我用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
非常量 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&)
.
将模板类型指定为 int
后,T = int
并且您的函数变为 void g(const int& x)
和 void g(int& x)
。接下来,编译器将执行模板类型推导。由于您传入 long
,编译器将尝试推断哪个函数适合 long
和 "best"。
由于 long
显然不是 int&
并且无法将 long
转换为 int&
,编译器 "throws" 的功能消失了,只有函数 g(const int& x)
被保留。但是,long
有可能被转换为 int
,这是编译器所做的,因为它是从 long
到 int
的隐式转换,它是一个 r-值,因此调用函数 g(const int& x)
。
简化您的代码以清楚地查看错误:
void g(int& x) { ... }
long y = 0;
g(y);
这将导致错误
cannot bind non-const lvalue reference of type 'int&' to an rvalue of
type 'int'
为什么?
一个非常量左值引用(即int&
)将仅绑定到非-相同类型的 const lvalues.
只有const
左值引用(即const int&
)可以绑定到右值(这是一个临时的long
隐式转换为 int
).
的对象
因此,
void g(const T& x) { std::cout << "1\n";}
将在您给定的代码中通过重载决议选择。
函数调用检查重载函数的最近匹配。
它检查输入参数的类型 - 在本例中为 long - 任何现有的非常量函数调用。 g(int&) 不匹配long,所以丢弃。
接下来它检查第二个函数 g(const int&)。这也不太符合。
它通过向下转换输入参数来检查它是否可以匹配其中任何一个。将 y 从 long 向下转换为 int。由于这种向下转换会产生一个右值,并且只有 const 左值引用可以绑定到右值 g(const int&) 被调用。
我认为@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;
}
以上代码片段输出以下结果:
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
。
当我用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
非常量 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&)
.
将模板类型指定为 int
后,T = int
并且您的函数变为 void g(const int& x)
和 void g(int& x)
。接下来,编译器将执行模板类型推导。由于您传入 long
,编译器将尝试推断哪个函数适合 long
和 "best"。
由于 long
显然不是 int&
并且无法将 long
转换为 int&
,编译器 "throws" 的功能消失了,只有函数 g(const int& x)
被保留。但是,long
有可能被转换为 int
,这是编译器所做的,因为它是从 long
到 int
的隐式转换,它是一个 r-值,因此调用函数 g(const int& x)
。
简化您的代码以清楚地查看错误:
void g(int& x) { ... }
long y = 0;
g(y);
这将导致错误
cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
为什么?
一个非常量左值引用(即int&
)将仅绑定到非-相同类型的 const lvalues.
只有const
左值引用(即const int&
)可以绑定到右值(这是一个临时的long
隐式转换为 int
).
因此,
void g(const T& x) { std::cout << "1\n";}
将在您给定的代码中通过重载决议选择。
函数调用检查重载函数的最近匹配。
它检查输入参数的类型 - 在本例中为 long - 任何现有的非常量函数调用。 g(int&) 不匹配long,所以丢弃。
接下来它检查第二个函数 g(const int&)。这也不太符合。
它通过向下转换输入参数来检查它是否可以匹配其中任何一个。将 y 从 long 向下转换为 int。由于这种向下转换会产生一个右值,并且只有 const 左值引用可以绑定到右值 g(const int&) 被调用。
我认为@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;
}
以上代码片段输出以下结果:
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
。