- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
在 clang 的 C++11 status page 中遇到了一个名为“*this 的右值引用”的提议.
我已经阅读了很多关于右值引用的内容并理解了它们,但我认为我对此一无所知。我在网络上也找不到太多使用这些术语的资源。
页面上有指向提案文件的链接:N2439 (将 move 语义扩展到 *this),但我也没有从那里得到太多例子。
这个功能是关于什么的?
最佳答案
首先,“*this 的 ref-qualifiers”只是一个“营销声明”。 *this
的类型永远不会改变,请参阅本文底部。不过用这种措辞更容易理解。
接下来,下面的代码根据函数†的“隐式对象参数”的ref-qualifier选择要调用的函数:
// t.cpp
#include <iostream>
struct test{
void f() &{ std::cout << "lvalue object\n"; }
void f() &&{ std::cout << "rvalue object\n"; }
};
int main(){
test t;
t.f(); // lvalue
test().f(); // rvalue
}
输出:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
整个过程是为了让您可以利用调用函数的对象是右值(例如,未命名的临时对象)这一事实。再以下面的代码为例:
struct test2{
std::unique_ptr<int[]> heavy_resource;
test2()
: heavy_resource(new int[500]) {}
operator std::unique_ptr<int[]>() const&{
// lvalue object, deep copy
std::unique_ptr<int[]> p(new int[500]);
for(int i=0; i < 500; ++i)
p[i] = heavy_resource[i];
return p;
}
operator std::unique_ptr<int[]>() &&{
// rvalue object
// we are garbage anyways, just move resource
return std::move(heavy_resource);
}
};
这可能有点做作,但您应该明白这一点。
请注意,您可以组合使用cv 限定符(const
和volatile
)和ref 限定符( &
和 &&
)。
注意:这里后面有很多标准引用和重载解析解释!
† 要理解这是如何工作的,以及为什么@Nicol Bolas 的答案至少有一部分是错误的,我们必须深入研究 C++ 标准(解释为什么@Nicol 的答案是错误的部分在底部,如果你只对那个感兴趣)。
要调用哪个函数由称为重载解析的过程决定。这个过程相当复杂,所以我们只触及对我们重要的部分。
首先,了解成员函数的重载解析如何工作很重要:
§13.3.1 [over.match.funcs]
p2 The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called. [...]
p3 Similarly, when appropriate, the context can construct an argument list that contains an implied object argument to denote the object to be operated on.
为什么我们甚至需要比较成员函数和非成员函数?运算符重载,这就是原因。考虑一下:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
您肯定希望下面的代码调用 free 函数,不是吗?
char const* s = "free foo!\n";
foo f;
f << s;
这就是为什么成员函数和非成员函数包含在所谓的重载集中的原因。为了使解决方案不那么复杂,存在标准报价的粗体部分。此外,这对我们来说很重要(相同的条款):
p4 For non-static member functions, the type of the implicit object parameter is
“lvalue reference to cv
X
” for functions declared without a ref-qualifier or with the&
ref-qualifier“rvalue reference to cv
X
” for functions declared with the&&
ref-qualifierwhere
X
is the class of which the function is a member and cv is the cv-qualification on the member function declaration. [...]p5 During overload resolution [...] [t]he implicit object parameter [...] retains its identity since conversions on the corresponding argument shall obey these additional rules:
no temporary object can be introduced to hold the argument for the implicit object parameter; and
no user-defined conversions can be applied to achieve a type match with it
[...]
(最后一点只是意味着您不能基于调用成员函数(或运算符)的对象的隐式转换来欺骗重载决策。)
让我们以本文开头的第一个示例为例。经过上述转换后,重载集看起来像这样:
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
然后包含隐含对象参数的参数列表与重载集中包含的每个函数的参数列表相匹配。在我们的例子中,参数列表将只包含该对象参数。让我们看看它是什么样子的:
// first call to 'f' in 'main'
test t;
f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)
// kept in overload-set
f2(t); // 't' not an rvalue, can't match 'test&&' (rvalue reference)
// taken out of overload-set
如果在测试集中的所有重载之后,只剩下一个,则重载解析成功,并调用链接到该转换后的重载的函数。第二次调用“f”也是如此:
// second call to 'f' in 'main'
f1(test()); // 'test()' not an lvalue, can't match 'test&' (lvalue reference)
// taken out of overload-set
f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)
// kept in overload-set
但是请注意,如果我们没有提供任何ref-qualifier(因此没有重载函数),f1
将匹配一个右值(仍然是 §13.3.1
):
p5 [...] For non-static member functions declared without a ref-qualifier, an additional rule applies:
- even if the implicit object parameter is not
const
-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
现在,谈谈为什么@Nicol 的回答至少有一部分是错误的。他说:
Note that this declaration changes the type of
*this
.
这是错误的,*this
是总是一个左值:
§5.3.1 [expr.unary.op] p1
The unary
*
operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
§9.3.2 [class.this] p1
In the body of a non-static (9.3) member function, the keyword
this
is a prvalue expression whose value is the address of the object for which the function is called. The type ofthis
in a member function of a classX
isX*
. [...]
关于c++ - 什么是 "rvalue reference for *this"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23508228/
They say Rvalues 的成员也是 Rvalues - 这很有意义。所以这要么是 VC++ 特有的错误,要么是我对右值理解的错误。 拿这个玩具代码: #include #include
(我搜索了[c++]“c++中的函数式编程”一书,只给出了4个不相关的结果。) 我正在阅读来自 Ivan Čukić 的 C++ 函数式编程 , 在第 118 页有一句话我不确定我是否理解。该节是关于
以下代码无法编译。 Clang 给出此错误消息:候选函数不可行:第一个参数没有从“A”到“A &&”的已知转换 这就好像 f() 中的 a 是一个左值。 struct A{}; void g(A&&
假设我有以下对象 class foo { foo() {..} //Constructor foo(const foo& f) {..} //Copy
我最近一直在玩 Rvalue 引用,但遇到了一个奇怪的问题。让我们定义一些名为 Foo 的简单类,其中包含一个 vector : class Foo { public: Foo(std::ve
RValue 引用的堆栈大小是多少? 例如在这样的情况下: struct A { /* ... */ }; A getA() { A temp; return
RValue 引用的堆栈大小是多少? 例如在这样的情况下: struct A { /* ... */ }; A getA() { A temp; return
现在我有一个只有一个构造函数的类 ShaderProgram(std::initializer_list> shaders); 我正在使用引用包装器,因为我没有引用的 initializer_list
我一直在努力理解移动构造函数场景背后发生的事情,但我的脑海中缺少了一些东西。 struct A { int s; A() : s(10) {} A(const A&
我有一个可变模板函数 template inline void ReadStream::decode(ARGS&...args) { internalDecode(args...); } tem
RValues 是不可操作的内存区域,因此像整数这样的文字被认为是 RValues。 常量构成 RValues 吗? const int x = 0; 至少可操作一次。 现在,编译器创建的临时对象也是
“*this 的右值引用”最典型的用例是什么?标准也将其称为成员函数的引用限定符? 顺便说一下,关于这个语言特性有一个非常好的解释 here 。 最佳答案 调用时,每个成员函数都有一个 *this 引
我有一个带有值的 vector : obj={1.0,2.0,3.0, 4.0,5.0,6.0 ,7.0,8.0,9.0,10.0} 假设,在数学上 obj 被划分为三个子 vector : obj=
考虑以下 C++ 代码 template void f (const int x, const int y, Args&&... args) { // Do something } 据我了解,这
考虑以下代码: int three() { return 3; } template class Foo { private: T* ptr; public: void ba
在 clang 的 C++11 status page 中遇到了一个名为“*this 的右值引用”的提议. 我已经阅读了很多关于右值引用的内容并理解了它们,但我认为我对此一无所知。我在网络上也找不到太
我遇到了下面的代码,但我在谷歌上找不到为什么下面的语句是有效的 C++: Base&& b = Derived(); 请解释或给出引用 这是一个示例代码: #include using namesp
在 clang 的 C++11 status page 中遇到了一个名为“*this 的右值引用”的提议. 我已经阅读了很多关于右值引用的内容并理解了它们,但我认为我对此一无所知。我在网络上也找不到太
我遇到了 RValue 不允许隐式转换的问题。我的问题是什么实现更好地“绕过”这个限制? 下面是说明问题的示例代码: template class ITestClass { public: vir
我想知道这怎么可能? template void Test(T&& arg) { arg = 14; } int a = 23; Test(a); 我的问题是函数 Test 需要一个右值类型的
我是一名优秀的程序员,十分优秀!