Rcpp 和 R:按引用传递

jfi*_*ler 2 r pass-by-reference pass-by-value rcpp

与 Rcpp 和 RI 合作观察到以下行为,目前我不明白。考虑以下用 Rcpp 编写的简单函数

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericMatrix hadamard_product(NumericMatrix & X, NumericMatrix & Y){
   unsigned int ncol = X.ncol();
   unsigned int nrow = X.nrow();
   int counter = 0;
   for (unsigned int j=0; j<ncol; j++) {
     for (unsigned int i=0; i<nrow; i++)  {
        X[counter++] *= Y(i, j);
     }
   }
   return X;
}
Run Code Online (Sandbox Code Playgroud)

这只是返回两个矩阵的按分量乘积。现在我知道这个函数的参数是通过引用传递的,即调用

M <- matrix(rnorm(4), ncol = 2)
N <- matrix(rnorm(4), ncol = 2)
M_copy <- M
hadamard_product(M, N)
Run Code Online (Sandbox Code Playgroud)

会覆盖原来的M。但是,它也会覆盖M_copy,我不明白。我认为M_copy <- M创建对象 M 的副本并将其保存在内存中的某个位置,而不是这个赋值将 M_copy 指向 M,这将是执行时的行为

x <- 1
y <- x
x <- 2
Run Code Online (Sandbox Code Playgroud)

例如。这不会改变 y,而只会改变 x。

那么为什么会出现上述行为呢?

Rol*_*and 5

不,R 不会立即进行复制,只有在必要时才进行复制,即copy-on-modify

x <- 1
tracemem(x)
#[1] "<0000000009A57D78>"
y <- x
tracemem(x)
#[1] "<0000000009A57D78>"
x <- 2
tracemem(x)
#[1] "<00000000099E9900>"
Run Code Online (Sandbox Code Playgroud)

由于您通过 R 外部的引用修改 M,R 无法知道副本是必要的。如果您想确保制作副本,可以使用data.table::copy。或者避免 C++ 代码中的副作用,例如,在其中进行深层复制(通过使用clone)。