Rcpp 中没有深拷贝,除非您使用clone 请求它。当您按值传递时,您正在创建一个新的 List 对象,但它使用相同的底层 R 对象。
因此,按值传递和按引用传递之间的差异很小。
但是,当你通过价值传递时,你必须为保护底层对象多付出一次代价。它可能会产生额外的成本,因为这个 Rcpp 依赖于递归不是很有效的R_PreserveObject。
我的指导方针是尽可能通过引用传递,这样您就不会支付额外的保护价格。如果您知道 ABCfun2 不会更改对象,我建议您通过引用 const 传递:ABCfun2( const List& )。如果您要更改List,那么我建议使用ABCfun2( List& )。
考虑这段代码:
#include <Rcpp.h>
using namespace Rcpp ;
#define DBG(MSG,X) Rprintf("%20s SEXP=<%p>. List=%p\n", MSG, (SEXP)X, &X ) ;
void fun_copy( List x, const char* idx ){
x[idx] = "foo" ;
DBG( "in fun_copy: ", x) ;
}
void fun_ref( List& x, const char* idx ){
x[idx] = "bar" ;
DBG( "in fun_ref: ", x) ;
}
// [[Rcpp::export]]
void test_copy(){
// create a list of 3 components
List data = List::create( _["a"] = 1, _["b"] = 2 ) ;
DBG( "initial: ", data) ;
fun_copy( data, "a") ;
DBG( "\nafter fun_copy (1): ", data) ;
// alter the 1st component of ths list, passed by value
fun_copy( data, "d") ;
DBG( "\nafter fun_copy (2): ", data) ;
}
// [[Rcpp::export]]
void test_ref(){
// create a list of 3 components
List data = List::create( _["a"] = 1, _["b"] = 2 ) ;
DBG( "initial: ", data) ;
fun_ref( data, "a") ;
DBG( "\nafter fun_ref (1): ", data) ;
// alter the 1st component of ths list, passed by value
fun_ref( data, "d") ;
DBG( "\nafter fun_ref (2): ", data) ;
}
我所做的只是将一个列表传递给一个函数,更新它并打印一些关于指向底层 R 对象的指针和指向 List 对象的指针 (this) 的信息。
以下是我调用test_copy 和test_ref 时发生的结果:
> test_copy()
initial: SEXP=<0x7ff97c26c278>. List=0x7fff5b909fd0
in fun_copy: SEXP=<0x7ff97c26c278>. List=0x7fff5b909f30
after fun_copy (1): SEXP=<0x7ff97c26c278>. List=0x7fff5b909fd0
$a
[1] "foo"
$b
[1] 2
in fun_copy: SEXP=<0x7ff97b2b3ed8>. List=0x7fff5b909f20
after fun_copy (2): SEXP=<0x7ff97c26c278>. List=0x7fff5b909fd0
$a
[1] "foo"
$b
[1] 2
我们从与 R 对象关联的现有列表开始。
initial: SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0
我们通过值将它传递给fun_copy,所以我们得到一个新的List,但使用相同的底层R对象:
in fun_copy: SEXP=<0x7fda4926d278>. List=0x7fff5bb5ef30
我们退出fun_copy。再次使用相同的底层 R 对象,然后回到我们原来的 List :
after fun_copy (1): SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0
现在我们再次调用fun_copy,但这次更新不在列表中的组件:x["d"]="foo"。
in fun_copy: SEXP=<0x7fda48989120>. List=0x7fff5bb5ef20
List别无选择,只能为自己创建一个新的底层R对象,但这个对象只是本地List的底层。因此,当我们离开get_copy 时,我们又回到了原来的List 及其原来的底层SEXP。
after fun_copy (2): SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0
这里的关键是第一次"a"已经上榜了,所以我们直接更新了数据。因为fun_copy 的本地对象和test_copy 的外部对象共享相同的底层R 对象,所以fun_copy 内部的修改被传播。
第二次,fun_copy 增长它的本地 List 对象,将其与不会传播到外部函数的全新 SEXP 相关联。
现在考虑当你通过引用传递时会发生什么:
> test_ref()
initial: SEXP=<0x7ff97c0e0f80>. List=0x7fff5b909fd0
in fun_ref: SEXP=<0x7ff97c0e0f80>. List=0x7fff5b909fd0
after fun_ref(1): SEXP=<0x7ff97c0e0f80>. List=0x7fff5b909fd0
$a
[1] "bar"
$b
[1] 2
in fun_ref: SEXP=<0x7ff97b5254c8>. List=0x7fff5b909fd0
after fun_ref(2): SEXP=<0x7ff97b5254c8>. List=0x7fff5b909fd0
$a
[1] "bar"
$b
[1] 2
$d
[1] "bar"
只有一个List 对象0x7fff5b909fd0。当我们必须在第二次调用中获得一个新的SEXP 时,它会正确地传播到外层。
对我来说,通过引用传递时的行为更容易理解。