我目前正在撰写我的学位论文,它还涉及C++ 11背后的理论的一些解释,这很好,因为C++是我选择的编程语言,而且标准或多或少是免费提供的(N3337)让你自己迷失在.
然而,我试图准确而详细地解释新的xvalue类别时遇到了障碍.我的理解是临时对象总是一个xvalue但我在标准中找不到任何对此的引用.我的理解是函数的表达式的值类别调用具有非引用返回类型的函数是xvalue.标准说"xvalue是涉及rvalue-references的某种表达式的结果",这让我烦恼.例如:
TestClass { ... };
testClass createObject() { return testClass(); }
void someFunction(TestClass& testClass) { ... }
void someFunction(TestClass&& testClass) { ... }
someFunction(createObject());
Run Code Online (Sandbox Code Playgroud)
正如所料,上面将调用带有rvalue-reference作为参数的重载函数.然而,createObject()不返回rvalue-reference,它返回一个TestClass类型的临时对象.我现在的问题是,我必须解释其背后的原因.表达式"createObject()"评估为什么?如果它确实是一个xvalue,因为它返回一个临时对象,它背后的推理是明确的,并且在重载解析期间有利于rvalue-reference.如果没有,那么关于标准的这种行为的解释是什么?是否有某些我尚未找到的隐式转换逻辑?
如果有人能帮我解决这个问题,我真的很感激,因为即使经过几天的挖掘和阅读,我还没有想出一个合理的解释.非常感谢提前.
我对这个问题的广泛性感到抱歉,只是所有这些细节都是紧密相连的。
我一直在尝试理解两个特定值类别 - xvalues 和 prvalues 之间的区别,但我仍然感到困惑。
不管怎样,我试图为自己开发的“同一性”概念的心智模型是,应该保证具有同一性的表达式驻留在实际程序的数据存储器中。
由于这个原因,字符串文字是左值,它们保证在整个程序运行期间驻留在内存中,而数字文字是纯右值,并且可以假设存储在直接汇编中。
这似乎同样适用于std::move纯纯右值文字,即,在调用时,fun(1)我们只会在被调用者框架中获得参数左值,但在调用时,fun(std::move(1))左值的 xvalue“种类”必须保留在调用者框架中。
然而,这种心理模型至少不适用于临时对象,据我所知,临时对象应该始终在实际内存中创建(例如,如果像使用纯右值参数那样调用fun(MyClass())右值引用函数)。所以我认为这种思维模式是错误的。
那么,思考 xvalue 的“同一性”属性的正确方法是什么?我已经读过,通过身份我可以比较地址,但是如果我可以比较 2 的地址MyClass().member(根据 cppreference 的 xvalue),比方说通过右值引用将它们传递到某个比较函数中,那么我不明白为什么我可以不对 2 MyClass()s(纯右值)做同样的事情吗?
与此相关的另一个来源是此处的答案: 什么是移动语义?
请注意,尽管 std::move(a) 是右值,但其求值不会创建临时对象。这个难题迫使委员会引入第三个价值类别。可以绑定到右值引用的东西,即使它不是传统意义上的右值,也称为xvalue(到期值)。
但这似乎与“可以比较地址”无关,并且a)我不明白这与右值的“传统意义上”有什么不同;b) 我不明白为什么这样的原因需要语言中的新值类别(好吧,这允许为 OO 意义上的对象提供动态类型,但 xvalue 不仅仅指对象)。
C++标准对"xvalues"(N4762§7.2.1.4)进行了以下说明:
表达式是xvalue,如果它是:
- ...
- 类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式是xvalue
考虑以下代码片段(使用Boost打印表达式的类型):
#include <iostream>
#include <boost/type_index.hpp>
using boost::typeindex::type_id_with_cvr;
struct X {
int var;
} x;
int main()
{
auto extended_type = type_id_with_cvr<decltype( std::move(x).var )>();
std::cout << extended_type.pretty_name() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是关于表达式std::move(x).var:
基于标准中的文本,我希望表达式是xvalue,但输出int不是int &&.
我在这里错过了什么?
在cpprefernce部分:值类别中,它声明"对象表达式的成员,其中a是rvalue,m是非引用类型的非静态数据成员"是xvalue.在标准(我发现于:N4140,草案C++ 14标准)中,它声明(在第87页)"如果表达式是......一个指定非静态数据成员的类成员访问表达式,则表达式是xvalue对象表达式为xvalue的非引用类型."
我想通过以下代码检查我对此的理解:
struct B { // B for Boring...
int i{0};
};
template <typename T> struct V {
V() { cout << "V" << endl; }
};
template <typename T> struct V<T &> { // Partial Specialization
V() { cout << "V&" << endl; }
};
template <typename T> struct V<T &&> { // Partial Specialization
V() { cout << "V&&" << endl; }
};
int main() {
int i{1};
V<decltype((1))> v1; // V
V<decltype((i))> …Run Code Online (Sandbox Code Playgroud) 我想计算x-value视图中两个正方形之间的距离.
我没有工作,因为在框架中也有,y-value并且不知道如何只选择一个.如何x-values从正方形的位置添加?
var redSquare: UIView = UIView()
var blueSquare: UIView = UIView()
var difference : Double = 0.0
func calc() {
difference = redSquare.frame - blueSquare.frame
}
Run Code Online (Sandbox Code Playgroud) 我在这里讲述一个长篇大论的背景故事,因为除了直接回答之外,我想知道导致这种情况的推理是否正确.
我有一个带dynamic_bitset<>参数的函数(来自Boost.dynamic_bitset).说它看起来像这样.
void foo(boost::dynamic_bitset<> db) {
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
碰巧它只是用构造函数构建的临时函数foo(boost::dynamic_bitset<>{5}.set())调用,如同(用5位bitset调用所有位设置).
我的位集只有少量位(少于32位).所以起初,我想"我只是按值传递它;副本比指针小." 但后来我想"它是动态的,所以它必须在堆上分配空间.我想避免不必要的分配和释放."
所以,我可以做到
void foo(const boost::dynamic_bitset<>& db);
Run Code Online (Sandbox Code Playgroud)
但是引用是一个指针,而dynamic_bitset(可能)有一个指向其数据的指针,因此使用dbwithin foo会经历两个间接级别,这看起来很愚蠢.显然,最好的方法是将指针复制到数据中foo,而无需重新分配和复制堆上的数据.
"啊哈!" 我说."当然这就是语义学的用途." 所以,我将签名更改为
void foo(boost::dynamic_bitset<>&& db);
Run Code Online (Sandbox Code Playgroud)
但是,调用foo(boost::dynamic_bitset<>{5}.set())会产生编译错误cannot bind 'boost::dynamic_bitset<>' lvalue to 'boost::dynamic_bitset<>&&'.我必须打电话
foo(std::move(boost::dynamic_bitset<>{5}.set()))
然后一切正常.
为什么我需要调用std :: move? 这似乎显然是一个xvalue(临时即将到期),不是吗?
当一个即将超出范围的变量被返回或抛出时,它的资源可以被重用,即它可以从 中移出,如下面的 C++ 程序所示:
#include <iostream>
struct X {
X() {
std::cout << "X()\n";
}
X(X&&) {
std::cout << "X(X&&)\n";
}
~X() {
std::cout << "~X()\n";
}
};
X f() {
X x;
std::cout << "return\n";
return x;
}
int main() {
try {
X x = f();
std::cout << "throw\n";
throw x;
} catch (...) {
std::cout << "catch\n";
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译(我们使用标志关闭复制/移动省略-fno-elide-constructors)、链接和执行:
clang++ -std=c++17 -O2 -Wall -pedantic -pthread -fno-elide-constructors\
main.cpp && ./a.out
Run Code Online (Sandbox Code Playgroud)
输出: …
遵循这个问题的公认答案Do rvalue references allow dangling references?当分配给右值引用左值时,xvalues 的生命周期似乎没有延长,就像问题中一样。但是,当我这样做时
#include <iostream>
using namespace std;
class Something {
public:
Something() {
cout << "Something()" << endl;
}
Something(const Something&) {
cout << "Something(const Something&)" << endl;
}
Something(Something&&) {
cout << "Something(Something&&)" << endl;
}
~Something() {
cout << "~Something()" << endl;
}
int a;
};
Something make_something() {
return Something{};
}
int main() {
auto&& something = make_something().a;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
调用返回的对象的生命周期make_something被延长,即使make_something().a是根据http://en.cppreference.com/w/cpp/language/value_category的 xvalue (xvalues …
从5.2.1.1开始:
表达式
E1[E2](根据定义)与*((E1)+(E2))[...]相同,除了在数组操作数的情况下,如果该操作数是左值,则结果为左值,否则为x值.
但是,使用以下代码:
struct S
{
int arr[5];
};
int main()
{
int &&r = S().arr[0];
}
Run Code Online (Sandbox Code Playgroud)
GCC和Clang都抱怨"rvalue引用不能绑定到lvalue int".
我误解了什么?据我所理解的S()是rvalue,S().arr是xvalue,所以也S().arr[0]应该是xvalue,并且应该能够绑定到rvalue refs.
#include <vector>
using namespace std;
struct TempData {
vector<int> data;
TempData() {
for(int i = 0; i < 100; i++) data.push_back(i);
}
// vector<int> GetData() { // function 1
// return move(data);
// }
vector<int>&& GetData() { // function 2
return move(data);
}
};
int main() {
vector<int> v;
{
TempData td;
v = td.GetData();
}
}
Run Code Online (Sandbox Code Playgroud)
function 1和 和有function 2什么区别?
将function 1构造一个 temp vector,move(data)然后将 temp 分配vector给v?
没有更多细节可以添加...