2014-01-03 4 views
6

Может ли кто-нибудь объяснить, когда ссылки rvalue предпочтительнее ссылок на const lvalue при работе в качестве параметров функции?(ссылка на rvalue) VS (const lvalue reference) как функциональные параметры в C++ 11

Background: Я пытался передать указатель const в функцию. Поскольку я должен рассматривать случаи, в которых передается локальный указатель и в котором передается временное (например, из возврата вызова функции), у меня есть два варианта: параметр может быть объявлен как:

void foo(T const* const&); //const lvalue ref to const ptr 

или

void foo(T const* &&); //rvalue ref to const ptr 

Но эта ссылка Rvalue не может быть привязан к локальной переменной (которая имеет Lvalue типа. Но я помню, Скотт Мейерс ввел термин «универсальный ссылка» для обозначения ссылки RValue. Это меня смущает больше.) Итак, мой вопрос заключается в том, что первая декларация может иметь дело с обоими случаями, когда второй вариант, использующий ссылку rvalue, будет предпочтительнее?

Примечание: В первом подходе, другие формы

void foo(const const T* &); 
void foo(const T* const&); 

не работает. Я предполагаю, что причина в том, что в последних двух я не был последовательным в том месте, где вставляются определители констант (пожалуйста, поправьте меня, если я ошибаюсь).

+4

http://stackoverflow.com/questions/17005423/c-rvalue-reference-and-const-qualifier может помочь, но ваш вопрос немного запутан. Кажется, вы смешиваете свободно указатель const и указатель на const. Если вам не нужно идеально переходить или перемещаться, rvalue refs не очень полезно. – Mat

+0

«Универсальные ссылки» применяются только при использовании ссылки с шаблоном. – Rastaban

ответ

1

Вы не хотите различать временный указатель и указатель lvalue. Это выглядит для меня как нечто, что обязательно провалится раньше, чем позже.

Универсальные Refs применяются только в шаблонных функций как

template<class T> void foo(T && fwdref); 

Обратите внимание, что «универсальный исх» является не такой же, как RValue исх.

+0

Спасибо, @ Мартин Ба! – CloudyTrees

2

Примечание: Я написал этот ответ в предположении, что T в вашем вопросе представляет некоторый фактический тип данных - я выбрал int в моих примерах ниже.

Background: Я пытался передать указатель const в функцию. [...] Я должен рассмотреть случаи, когда пропускается локальный указатель и в котором проходит временное (например, от возврата вызова функции)

Вы не сказали, что вы подразумеваете под "const pointer". Сначала я предполагаю, что вы имеете в виду указатель, который сам по себе является постоянным (т. Е. Адрес, на который он указывает, не может быть изменен).

По вашему описанию, есть в основном два способа получить такой указатель:

// Case 1 (what you call a local pointer -- this should be inside some 
//   function body): 
int *const p = 0; 

// Case 2, a function that returns a pointer; this is your rvalue case 
// in contexts where f() is called and its return value used as a temporary: 
int *f() 
{ return 0; } 

// Note: The temporary returned by this function isn't, strictly speaking, 
//  constant. It could be modified as long as it is alive. But from 
//  your description I take it that you have no intentions of doing so 
//  and/or regard temporaries as generally constant. 

Теперь вы можете определить функцию, которая принимает эти два случая следующим образом:

void g(int *const &arg) 
{ } 

Вы можете примените это как g(p); к постоянному локальному указателю, такому как p, определенному ранее, а также к временному g(f());.(Вы можете, в-третьих, применить его к неконстантного местного указателя, а также, потому что происходит от неконстантного Lvalue к сопзЬ Lvalue никогда не является проблемой.)

Эта функция g имеет аргумент функции arg который определяется как постоянная ссылка на значение int. Он может привязываться к постоянному (или действительно непостоянному) локальному указателю (например, p), а также к временному, поскольку константные ссылки lvalue, в отличие от ссылок на константу lvalue, могут это сделать.

Примечание: Непонятно, почему в этом случае вам нужно, чтобы аргумент функции был ссылкой. Вы можете просто объявить void g(int *const arg) (без амперсанда) и без справки. Причины включают: а) вы все равно не можете его изменить; б) Во всех реалиях реального мира эта ссылка займет столько же (или как мало) место, что и сам указатель, поэтому нет смысла избегать копирования.

В любом случае. Если вы хотите, вы можете также определить вторую версию g специально для ссылок RValue:

void g(int *&& arg) 
{ } 

Это может только применяться к временным, не к локальному указателю, так как аргумент функции определяется как RValue ссылка, которая может привязываться к временным, но не к lvalues.

Однако, если указателем «const const» вы на самом деле означаете указатель-на-const, то есть указатель, который может быть изменен на разные адреса, но не имеет права изменять значение, хранящееся на этих адресах, объявления немного отличаются. Ключевое слово const должно быть затем поставить перед звездочкой, и для большей наглядности лучше перед спецификатором типа int:

// Declare local pointer-to-const: 
const int *p = 0; 

// Function that returns a pointer-to-const: 
const int *f() 
{ return 0; } 

Функция, которая может принять эти два будут затем быть объявлен как:

void g(const int *const &arg) 
{ } 

первый const означает, что мы говорим о указателях-константах, а второй const гарантирует, что мы имеем константу lvalue-reference, которая может связываться как с rvalues, так и с lvalues. Обратите внимание, что эта функция не может изменить то, что указывает arg, потому что arg объявлен как константная ссылка lvalue. В случае, когда arg привязывается к временному, это, вероятно, то, что мы хотим в любом случае (как указано выше). Но в случае, когда функция называется g(p);, мы могли бы захотеть изменить локальный указатель p с точностью до g. Если вы хотите g иметь эту силу, необходимо определить две версии этого:

void g(const int *&& arg) 
{ /* Can bind to temporaries, but not modify them. */ } 

void g(const int *& arg) 
{ /* Can bind to local variables and modify what they point at */ } 

Примечание 1: Ваша первоначальная декларация const int *const &const бесполезно (и даже не принято НКУ). Это означало бы «постоянную ссылку на постоянный указатель на константу int», но поскольку ссылка на константный указатель сама по себе не является константой-ссылкой, окончательный const является излишним (и не предусмотренным стандартом).

Примечание 2: Универсальные ссылки - это не то же самое, что ссылки на rvalue. Универсальные ссылки объявляются как T &&arg, где T является параметром шаблона.В зависимости от того, что T относится к каждому экземпляру шаблона, это может быть ссылка на lvalue или ссылка на rvalue, следовательно, ее «универсальный» символ. В любом случае, это не имеет никакого отношения к вашему варианту использования, поскольку вы имеете дело с указателями T * (даже если предположить, что T является параметром шаблона).

+0

Nitpick: «Ключевое слово const должно быть помещено перед спецификатором типа int:» - это не совсем правильно. Это должно быть до '*'. 'int const *' и 'const int *' являются одним и тем же типом (указатель на const int). (Первая версия имеет то преимущество, что ее можно читать справа налево.) – Mat

+0

Значение rvalue lvalue применяется к указателю, а не к 'T', поэтому не имеет значения, является ли' T' параметром 'template'. – Yakk

+0

@Mat True. Будет редактировать. – jogojapan

6

Очень редко бывает передать указатель на const &: в лучшем случае он берет одни и те же накладные расходы, в худшем случае он вызывает чрезвычайно сложную логику повторного поиска poimter, чтобы удивить читателей вашего кода.

Принимать указатели по значению - T const* - и все более разумно.

Ссылки на значения, отличные от указателей, имеют больше смысла.

Универсальные ссылки - это метод, использующий ссылки rvalue и lvalue в контексте вывода типа. Он в основном применяется только в том случае, если вы вывели тип T&& из выражения - в этом контексте T может быть X, X& или X const& (или другими вариантами cv).

Если T - X& или X const&, ссылка rvalue на ссылку lvalue сворачивается в ссылку lvalue. Это пример стандартного комитета, который умеет, и он позволяет использовать универсальные эталонные переменные auto&&x=, а отличный код пересылки легко записать.

+0

Я согласен, что использование указателя по const-ссылке не очень полезно (как указано в моем ответе). Но можете ли вы объяснить «чрезвычайно сложную логику повторного поиска указателя»? – jogojapan

+0

@jogojapan Предположим, что упомянутый указатель изменен в какой-то момент в середине функции: он реализуется. Обработка этой возможности требует пересмотра ваших предположений о указателе в каждой строке функции с выполнением нелокального кода. Только если это произойдет, это два разных (или если ref сохраняется после вызова функции) – Yakk

+0

Я не понимаю вторую часть вашего комментария, но рассматриваю первую часть: мы говорим о постоянном указателе (или указателе константы указателя). Как это можно перекрыть? – jogojapan

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