R言語では、関数の引数は原則すべて値渡し。なので、関数を呼ぶたびに変数のコピーが作られている。
C++でも原則は値渡しで、参照渡しにするには"&"をつけて指定する。
とすればRcppで関数を作った場合もそうか思っていたが、違った。
-
IntegerVector
NumericMatrix
など、Rcppで定義されたクラスの変数は参照渡しになる - ただし、変数の型変換が必要なケース、たとえば、
IntegerVector
で定義されている引数に実数ベクトルを与えると、参照が崩れて値渡しになる
Rユーザーは普段、実数と整数を意識していないことも多いので場合によっては想定外の挙動をする可能性がある。ご注意あれ。
##例
与えられた引数の第1要素を2に変更するという関数をRcppを用いて定義している。引数が値渡しであればこの結果は関数の呼び出し元には影響せず、参照渡しであればもとの変数も変更される。
比較のために色々なタイプの引数を用いている。
要約すると
- C++由来のタイプの引数については、すべて値渡し。関数内の変更は呼び出し元へは影響しない
- タイプ通りの引数、つまり、
IntegerVector
の引数に整数ベクトルを与えたり、NumericVector
の引数に実数ベクトルを与えると、参照渡しになり、関数内の変更が呼び出し元へ影響する - 異なるタイプの引数、つまり、
IntegerVector
の引数に実数ベクトルを与えたり、NumericVector
の引数に整数ベクトルを与えると、値渡しになり、関数内の変更が呼び出し元へ影響しない
library(Rcpp)
cppFunction(
"
void argument_reference(int x, double y,
IntegerVector z, NumericVector w,
std::vector<int> v, std::vector<double> u) {
x = 2;
y = 2;
z(0) = 2;
w(0) = 2;
v[0] = 2;
u[0] = 2;
}
"
)
## input real
x <- 1
y <- 1
z <- c(1, 1, 1)
w <- c(1, 1, 1)
v <- c(1, 1, 1)
u <- c(1, 1, 1)
argument_reference(x, y, z, w, v, u)
x
##[1] 1
y
##[1] 1
z
##[1] 1 1 1
w
##[1] 2 1 1
v
##[1] 1 1 1
u
##[1] 1 1 1
## input integer
x <- 1L
y <- 1L
z <- c(1L, 1L, 1L)
w <- c(1L, 1L, 1L)
v <- c(1L, 1L, 1L)
u <- c(1L, 1L, 1L)
argument_reference(x, y, z, w, v, u)
x
##[1] 1
y
##[1] 1
z
##[1] 2 1 1
w
##[1] 1 1 1
v
##[1] 1 1 1
u
##[1] 1 1 1