2016-05-23 2 views
3

Я использую RcppArmadillo::sample в моем коде Rcpp, который имеет это странное поведение ниже. fun_good работает как ожидалось, выборка 1 элемент из x вектор. Тем не менее, fun_bad не работает, хотя единственная разница в том, что я не создаю вектор-источник x заранее.Требуется ли для экземпляра RcppArmadillo создать аргумент заранее?

#include <RcppArmadilloExtensions/sample.h> 
// [[Rcpp::depends(RcppArmadillo)]] 

using namespace Rcpp; 

// [[Rcpp::export]] 
IntegerVector fun_good() { 
    IntegerVector x = seq_len(5); 
    IntegerVector newOffer = RcppArmadillo::sample(x, 1, true); 
    return newOffer; 
} 

// [[Rcpp::export]] 
IntegerVector fun_bad() { 
    IntegerVector newOffer = RcppArmadillo::sample(seq_len(5), 1, true); 
    return newOffer; 
} 

Сообщение об ошибке lvalue required as left operand of assignment, и указывает на следующий источник. Почему бы ret[ii] не было назначено в fun_bad?

// copy the results into the return vector 
     for (ii=0; ii<size; ii++) { 
      jj = index[ii]; 
      ret[ii] = x[jj]; 
     } 
     return(ret); 

ответ

4

TL; DR

сделать явное приведение (как cdeterman сделал) или явный вызов конструктора:

// [[Rcpp::export]] 
Rcpp::IntegerVector fun_bad() { 
    Rcpp::IntegerVector newOffer = 
     RcppArmadillo::sample(Rcpp::IntegerVector(seq_len(5)), 1, true); 
    return newOffer; 
} 

Не цитируйте меня на специфику, но я m довольно уверен, что вы столкнулись с краевым случаем Expression Templates, не играющим красиво с правилами вычитания типа шаблона. Во-первых, соответствующая часть сообщения об ошибке, излучаемый мой компилятор:

... В конкретизации «T Rcpp :: RcppArmadillo :: образца (сопзЬ T &, INT, BOOL, Rcpp :: NumericVector) [с T = Rcpp :: sugar :: SeqLen; ...

Следовательно, в templated sample function, T выводится как имеющий тип Rcpp::sugar::SeqLen.

SeqLen является шаблонным классом выражения - defined here - которые, в большинстве случаев, будет (неявно) преобразуется в Rcpp::IntegerVector (из-за его наследство от Rcpp::VectorBase<INTSXP, ...>).Например,

// [[Rcpp::export]] 
Rcpp::IntegerVector test(int n = 5) { 
    return Rcpp::seq_len(5); // Ok 
} 

Однако, поскольку неявные преобразования являются частью процесса разрешения перегрузки, а не процесс дедукции типа шаблона, T выводится точно так, как Rcpp::sugar::SeqLen - значит это выражение

ret[ii] = x[jj]; 

является вызывающий Rcpp::sugar::SeqLen::operator[]неRcpp::Vector::operator[], как это обычно бывает), который производит rvalue (см. ниже †).

Вы могли заметить, что в отличие от некоторых классов ET sugar, SeqLen является более «истинным» шаблон выражения в том, что он просто обеспечивает operator[] для того, лениво оценены. Он не сохраняет постоянный элемент ссылочных данных/предоставляет оператор векторного преобразования (как, например, cumprod и многие другие); она буквально используется для построения вектора - this constructor, если я не ошибаюсь,

template <bool NA, typename VEC> 
Vector(const VectorBase<RTYPE,NA,VEC>& other) { 
    RCPP_DEBUG_2("Vector<%d>(const VectorBase<RTYPE,NA,VEC>&) [VEC = %s]", RTYPE, DEMANGLE(VEC)) 
    import_sugar_expression(other, typename traits::same_type<Vector,VEC>::type()) ; 
} 

, который использует следующие вспомогательные методы, определенные в классе Vector:

// we are importing a real sugar expression, i.e. not a vector 
template <bool NA, typename VEC> 
inline void import_sugar_expression(const Rcpp::VectorBase<RTYPE,NA,VEC>& other, traits::false_type) { 
    RCPP_DEBUG_4("Vector<%d>::import_sugar_expression(VectorBase<%d,%d,%s>, false_type)", RTYPE, NA, RTYPE, DEMANGLE(VEC)) ; 
    R_xlen_t n = other.size() ; 
    Storage::set__(Rf_allocVector(RTYPE, n)) ; 
    import_expression<VEC>(other.get_ref() , n) ; 
} 

template <typename T> 
inline void import_expression(const T& other, int n) { 
    iterator start = begin() ; 
    RCPP_LOOP_UNROLL(start,other) 
} 

Во всяком случае, до тех пор Rcpp автоматически генерирует фактический векторный объект из выражения sugar::SeqLen, он недоступен для использования (по крайней мере, так, как это требуется в этом конкретном выражении: ret[ii] = x[jj];).


† Так же, как проверки вменяемости, мы можем использовать несколько C++ 11 метапрограммирования конструкций изучить разницу между возвращаемых значений SeqLen::operator[] и Vector::operator[]:

// [[Rcpp::plugins(cpp11)]] 
// [[Rcpp::depends(RcppArmadillo)]] 
#include <RcppArmadilloExtensions/sample.h> 

typedef decltype(Rcpp::sugar::SeqLen(1)[0]) rvalue_t; 
typedef decltype(Rcpp::IntegerVector::create(1)[0]) lvalue_ref_t; 

// [[Rcpp::export]] 
void test() { 
    // rvalue_t is an rvalue 
    Rcpp::Rcout 
     << std::is_rvalue_reference<rvalue_t&&>::value 
     << "\n"; 
    // lvalue_ref_t is an lvalue 
    Rcpp::Rcout 
     << std::is_lvalue_reference<lvalue_ref_t>::value 
     << "\n"; 

    // rvalue_t is _not_ assignable 
    Rcpp::Rcout 
     << std::is_assignable<rvalue_t, R_xlen_t>::value 
     << "\n"; 
    // lvalue_ref_t is assignable 
    Rcpp::Rcout 
     << std::is_assignable<lvalue_ref_t, R_xlen_t>::value 
     << "\n"; 
} 

/*** R 

test() 
# 1  ## true 
# 1  ## true 
# 0  ## false 
# 1  ## true 

*/ 
+2

Хорошая работа, как обычно. Upvoted. –

2

Хотя я не могу предоставить явную причину, почему это происходит, но если вы явно указать, что seq_len выход является IntegerVector функция компилируется и работает, как ожидалось.

// [[Rcpp::export]] 
IntegerVector fun_bad() { 
    IntegerVector newOffer = RcppArmadillo::sample((IntegerVector)seq_len(5), 1, true); 
    return newOffer; 
} 

Я думаю, это потому, что seq_len вызов в sample временный объект и поэтому rvalue. Явно выводя результат как IntegerVector, похоже, делает его lvalue, поэтому он работает. Опять же, почему это так, я не знаю.

Я уверен, что Дирк или кто-то, у кого больше знаний на С ++, чем я, придет, чтобы дать более явный ответ в конце концов, если вы подождете достаточно долго.

+0

Иногда вы только что получили, чтобы помочь компилятор. –

Смежные вопросы