我认为当通用引用参数与rvalue引用参数匹配时,将返回rvalue引用参数。但是,我的测试显示rvalue引用被通用引用函数模板转换为左值引用。为什么会这样?
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
T f1(T&&t) { //<-----this is a universal reference
cout << "is_lvalue reference:" << is_lvalue_reference<T>::value << endl;
cout << "is_rvalue reference:" << is_rvalue_reference<T>::value << endl;
cout << "is_reference:" << is_reference<T>::value << endl;
return t;
}
void f2(int&& t) {
cout << "f2 is_lvalue reference:" << is_lvalue_reference<decltype(t)>::value << endl;
cout << "f2 is_rvalue reference:" << is_rvalue_reference<decltype(t)>::value << endl;
cout << "f2 is_reference:" << is_reference<decltype(t)>::value << endl;
f1(t);
}
int main()
{
f2(5);
return 0;
}
在GCC和VC ++ 2010中,结果如下:
f2 is_lvalue reference:0
f2 is_rvalue reference:1
f2 is_reference:1
is_lvalue reference:1
is_rvalue reference:0
is_reference:1
换句话说,t
中的参数f2
是右值引用,但是当传递给f1
时,参数变为左值引用。它不应该保留f1
中的右值 -
答案 0 :(得分:6)
原因是命名的右值引用被视为左值。
将 t 传递给 f1 以保留右值时,您应该在 f2 中使用std::move:
void f2(int&& t) {
f1(std::move(t));
}
Here你可以找到一个很好的解释。
答案 1 :(得分:3)
调用f1(t)
,参数是表达式t
。不是static_cast<decltype(t)>(t)
或者其他什么。您对decltype(t)
的检查与f1(t)
的调用无关。
表达式t
的类型为int
,值类别为左值。 (根据经验,如果你可以获取表达式的地址,那么它就是一个左值,你当然可以写&t
)。 &#34;信息&#34;引用变量最初被声明为引用只能通过decltype
检查才能看到。
由于使用左值调用f1
,T
推断为int&
。
NB。如果您希望在f1
中看到decltype(t)
成立,您可能希望T
也使用is_rvalue_reference
而不是f1
。对于右值参数,T
推导为非引用类型,例如如果您通过f2
进行f1(std::move(t));
修正f1
,则T
int
decltype(t)
f1
和int&&
def baseDir = System.getProperty( 'catalina.base' )
def imagesDir = "${baseDir}\webapps\myapp\images"
}是@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkPermission()) {
//do your work
} else {
requestPermission();
}
}
}
protected boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (result == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
}
protected void requestPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, "Write External Storage permission allows us to do store images. Please allow this permission in App Settings.", Toast.LENGTH_LONG).show();
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 100:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//do your work
} else {
Log.e("value", "Permission Denied, You cannot use local drive .");
}
break;
}
}
。
答案 2 :(得分:0)
在研究了C ++ 11标准之后,我对f1(t);
f2
之后发生的事情有了一个模糊的概念。我在这里描述一下,看看我是否做对了:
f2
中,t
是int&&
类型的左值(不是int
,这是一个重要区别)调用f1(t);
导致类型被推断为:
2.1当T
中的f1
被赋予左值时,它被推断为该左值的类型或int&& &
2.2参考折叠导致int&& &
成为int &
。这是T
。
由于f1
的参数被声明为T&&
,因此t
中参数f1
的类型为int & &&
。因此,引用折叠是第二次将t
的类型推断为int &
。
因此,T = int &
,参数t
的类型为int &
。即参数t
是int &
有任何评论吗?
答案 3 :(得分:-1)
使用 std::forward 将 t 保持为右值。
void f2(int&& t) {
//....
f1(std::forward<int>(t));
}
t 参数作为右值传递并推导出为 int 类型。 (看看如何扣除here)
std::forward(t) 返回一个 int&& 类型的右值,以便调用 f1(int&&)
没有 std::forward(t),f1(t) 接受 int 类型的参数并调用 f1(int&)
更新:请注意
之间的区别 is_lvalue_reference<T> and is_lvalue_reference<decltype<t>>.
在模板中,T是根据函数参数推导出来的,而t的值类别在f2中总是左值,
forward<int>(t) and move(t)
总是右值。
#include <iostream>
#include <type_traits>
class A {};
template <typename T>
T f0(T& t) { //<-----this is a universal reference
std::cout<< "f0 lvalue"<<'\n';
// take T as template argument, it is type int, so not rvalue, not lvalue.
// std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
std::cout << "is_rvalue reference:" << std::is_rvalue_reference<T>::value << std::endl;
std::cout << "is_reference:" << std::is_reference<T>::value << std::endl;
return t;
}
template <typename T>
T f0(T&&t) { //<-----this is a universal reference
std::cout<< "f0 rvalue"<<'\n';
std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
std::cout << "is_rvalue reference:" << std::is_rvalue_reference<T>::value << std::endl;
std::cout << "is_reference:" << std::is_reference<T>::value << std::endl;
return t;
}
template <typename T>
T f1(T& t) { //<-----this is a universal reference
std::cout<< "f1 lvalue"<<'\n';
// take T as template argument, it is type int, so not rvalue, not lvalue.
// std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
std::cout << "is_lvalue reference:" << std::is_lvalue_reference<decltype(t)>::value << std::endl;
std::cout << "is_rvalue reference:" << std::is_rvalue_reference<decltype(t)>::value << std::endl;
std::cout << "is_reference:" << std::is_reference<decltype(t)>::value << std::endl;
return t;
}
template <typename T>
T f1(T&&t) { //<-----this is a universal reference
std::cout<< "f1 rvalue"<<'\n';
std::cout << "is_lvalue reference:" << std::is_lvalue_reference<decltype(t)>::value << std::endl;
std::cout << "is_rvalue reference:" << std::is_rvalue_reference<decltype(t)>::value << std::endl;
std::cout << "is_reference:" << std::is_reference<decltype(t)>::value << std::endl;
return t;
}
void f2(int&&t) { //<-----this is a universal reference
std::cout<< "f2 rvalue"<<'\n';
std::cout << "is_lvalue reference:" << std::is_lvalue_reference<decltype(t)>::value << std::endl;
std::cout << "is_rvalue reference:" << std::is_rvalue_reference<decltype(t)>::value << std::endl;
std::cout << "is_reference:" << std::is_reference<decltype(t)>::value << std::endl;
f1(std::forward<int>(t)); // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
f1(std::move(t)); // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
f1(t); // T is deduced as int for f(T&), type int is not rvalue, nor lvalue, t is lvalue
//if f1(T&) not exist, then f1(t) will call f1(T&&), T is deduced as int&, type int& is lvalue, t is lvalue
f0(std::forward<int>(t)); // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
f0(std::move(t)); // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
f0(t); // T is deduced as int for f(T&), type int is not rvalue, nor lvalue, t is lvalue
//if f0(T&) not exist, then f0(t) will call f0(T&&), T is deduced as int&, type int& is lvalue, t is lvalue
}
void test_rvalue()
{
f2(5);
std::cout << std::boolalpha;
std::cout << std::is_lvalue_reference<A>::value << '\n'; // A is not lvalue
std::cout << std::is_rvalue_reference<A>::value << '\n'; // A is not rvalue
std::cout << std::is_lvalue_reference<A&>::value << '\n'; // A& is lvalue
std::cout << std::is_rvalue_reference<A&&>::value << '\n'; // A&& is rvalue
std::cout << std::is_lvalue_reference<int>::value << '\n'; // int is not lvalue
std::cout << std::is_rvalue_reference<int>::value << '\n'; // int is not rvalue
std::cout << std::is_lvalue_reference<int&>::value << '\n'; // int& is lvalue
std::cout << std::is_rvalue_reference<int&&>::value << '\n'; // int&& is rvalue
}