我试图理解
ref
和 const ref
应用于复合类型成员(例如记录)的行为。这里,可以假设它们的行为类似于 C++ 中的 auto&
和 const auto&
吗?即,即使对于 const ref
情况也不会创建副本?我想知道在某些情况下,编译器是否可以通过优化为 const ref
创建临时变量(尽管我猜这不会发生)。
为了获得一些经验,我尝试了以下代码并确认这个简单案例的地址是相同的。
use CTypes;
record Foo {
var n = 100;
}
var foo: Foo;
writeln("foo.n : addr = ", c_ptrToConst(foo.n));
ref n_ref = foo.n;
writeln("n_ref : addr = ", c_ptrToConst(n_ref));
const ref n_cref = foo.n;
writeln("n_cref : addr = ", c_ptrToConst(n_cref));
(Result)
foo.n : addr = 0x654816445110
n_ref : addr = 0x654816445110
n_cref : addr = 0x654816445110
另外,为了检查数据的地址,在CTypes模块中使用
c_ptrToConst()
来编写类似于C++中的以下代码是否合理?
#include <iostream>
using namespace std;
struct Foo {
int n = 100;
};
int main() {
Foo foo;
cout << "foo.n : addr = " << &foo.n << endl;
auto& n_ref = foo.n;
cout << "n_ref : addr = " << &n_ref << endl;
const auto& n_cref = foo.n;
cout << "n_cref : addr = " << &n_cref << endl;
}
一个相关的问题是:鉴于
ref
和 const ref
不进行复制,直接使用数组组件(例如 foo.arr
)或使用 ref
变量(ref arr = foo.arr
)之间是否有性能差异用于执行相同的数组计算? (这里,foo
应该有一个数组组件arr
。)
在我看来,你的想法是正确的。 我认为 Chapel 中的
ref
和 const ref
就像 C 指针,但不需要 *
来取消引用或 &
来建立。 初始化它会将其指向某个东西(并且以后不能将其重新指向其他东西),并且随后对它的引用就像对该指针的取消引用一样。
由于 Chapel 支持分布式内存编程和全局命名空间,Chapel 引用可以引用存储在远程内存中(例如,远程计算节点上)的数据以及本地存储的变量,这使得它们比 C/C++ 更强大。
请注意,虽然您的示例使用 Chapel 的类型推断,这使得它们类似于您在 C++ 中提到的
auto
情况,但可以键入 Chapel 中的 ref
声明。 例如:
var x = 42;
ref y: int = x;
在这里,Chapel 推断
x
是一个 int
,因为它是用 int
文字初始化的。 虽然 y
可以类似地从 x 的类型推断出来,但在这里我断言它是 ref
到 int
。 虽然这在像这样的小情况下很明显,但在初始化表达式更复杂的情况下,这种类型注释可以作为文档或代码安全断言很有帮助。
我相当确定,今天 Chapel 编译器不会创建
const ref
引用的变量的深层副本,如果我们将来要更改此设置,我会感到惊讶。 具体来说,这样做似乎与用户明确要求的内容相矛盾。 如果 Chapel 将来这样做,我预计只会在它作为优化的情况下,用户无法确定我们已经这样做了(比如您使用 c_ptrToConst()
.
对于您的其他问题:
对于记录来说,使用
c_ptrTo()
/c_ptrToConst()
或 c_addrOf()
/c_addrOfConst()
是获取指向 Chapel 变量的 C 指针/const 指针的合理且等效的方法。 如您所知,对于其他类型,两者可能有所不同。 例如,对于本地连续数组,c_ptrTo()
将返回指向数组缓冲区中第一个元素的指针,而c_addrOf()
将返回指向数组元数据的指针。
对于记录字段的
ref
,我通常不希望使用 foo.field
与 refToField
访问字段之间存在太大的性能差异,无论字段的类型如何。 我可以想象潜在差异的一种情况是,如果记录是静态分配的,则编译器知道 foo.field
的确切地址,但必须取消引用 refToField
的逻辑指针才能到达它。 但实际上,我预计这些情况不会经常出现,并且差异可以忽略不计。 具体来说,我经常使用 ref
或 const ref
作为为更复杂的表达式创建简写的方法。
裁判可以相当 有价值的是当表达式的位置对于 计算。 例如,像
A[i, j][k]
这样的复杂数组表达式将
往往涉及地址算术和指针取消引用,例如存储
如果要使用该元素,则对该元素的引用可能很有价值
在一段代码中多次出现。 同样,ref
也很有用
当通过多个其他类字段(例如,二叉树中的root.left.right.right
)访问一个类并且需要访问该类时
场几次。 在这两种情况下,ref
都会给你们两个
表达式的简写以及直接指向其的更便宜的方法
内存中的位置。