Почему субвектор должен быть const, когда он передается по ссылке? [Дубликат]

  LINES TERMINATED by '\r \n'  

Это было ключом к успеху для меня при использовании текстового файла

190
задан curiousguy 6 December 2011 в 16:23
поделиться

11 ответов

Из этой статьи в блоге Visual C ++ о ссылках rvalue :

... C ++ не хочет, чтобы вы случайно изменяли временные разделы, но напрямую вызывали не- Функция const member на изменяемом rvalue является явной, поэтому разрешено ...

В принципе, вы не должны пытаться изменять временные разделы по той причине, что они являются временными объектами и будут умирать момент сейчас. Причина, по которой вам разрешено вызывать неконстантные методы, - это то, что вы можете делать какие-то «глупые» вещи, пока знаете, что делаете, и о них явно (например, используя reinterpret_cast). Но если вы привязываете временную ссылку на неконстантную ссылку, вы можете продолжать ее прокручивать «навсегда» только для того, чтобы ваши манипуляции с объектом исчезли, потому что где-то по пути вы полностью забыли, что это было временным.

Если бы я был вами, я бы переосмыслил дизайн своих функций. Почему g () принимает ссылку, изменяет ли параметр? Если нет, сделайте это ссылкой на const, если да, то почему вы пытаетесь временно переходить к нему, разве вам не важно, что вы временно изменяете? Почему getx () возвращается в любом случае временно? Если вы поделитесь с нами своим реальным сценарием и тем, что вы пытаетесь выполнить, вы можете получить некоторые полезные советы о том, как это сделать.

Против языка и обмана компилятора редко решает проблемы - обычно он создает проблемы.

Редактировать: адресация вопросов в комментарии: 1) X& x = getx().ref(); // OK when will x die? - я не знаю, и мне все равно, потому что это именно то, что я имею в виду под «идеей против языка». Язык говорит, что «временные члены умирают в конце утверждения, если только они не связаны с константой ссылки, и в этом случае они умирают, когда ссылка выходит за рамки». Применяя это правило, кажется, что x уже мертв в начале следующего оператора, так как он не привязан к const reference (компилятор не знает, что возвращает ref ()). Однако это только предположение.

2) Я четко сформулировал цель: вам не разрешено изменять временные рамки, потому что это просто не имеет смысла (игнорируя ссылки на C ++ 0x rvalue). Вопрос: «Тогда почему мне позволено называть неконстантных членов?» является хорошим, но у меня нет лучшего ответа, чем тот, который я уже сказал выше.

3) Хорошо, если я прав относительно x в X& x = getx().ref();, умирающем в конце утверждения, проблемы очевидны.

В любом случае, исходя из вашего вопроса и комментариев, я не думаю, что даже эти дополнительные ответы вас устраивают. Вот окончательная попытка / сводка: комитет C ++ решил, что не имеет смысла изменять временные рамки, поэтому они запрещают привязку к неконстантным ссылкам. Может быть, некоторые реализации компилятора или исторические проблемы также были задействованы, я не знаю. Затем возник какой-то конкретный случай, и было решено, что, несмотря ни на что, они по-прежнему позволят прямое изменение путем вызова метода non-const. Но это исключение - вам обычно не разрешается изменять временные рамки. Да, C ++ часто бывает странным.

83
ответ дан sbk 17 August 2018 в 09:35
поделиться
  • 1
    @sbk: 1) Собственно, правильная фраза: & quot; ... в конце полного выражения ... ". Полагаю, что «полное выражение», как определено, является не суб-выражением какого-либо другого выражения. Является ли это всегда таким же, как «конец заявления», я не уверен. – sbi 14 October 2009 в 17:46
  • 2
    @sbk: 2) На самом деле вам являются разрешено изменять rvalues ​​(временные). Это запрещено для встроенных типов (int и т. Д.), Но разрешено для пользовательских типов: (std::string("A")+"B").append("C"). – sbi 14 October 2009 в 17:51
  • 3
    @sbk: 3) Причина, по которой Stroustrup дает (в D & amp; E) запрет на привязку значений r к неконстантным ссылкам, заключается в том, что если g() Alexey изменит объект (который вы ожидаете от функции, -const), он изменит объект, который умрет, поэтому никто не может получить модифицированное значение в любом случае. Он говорит, что это, скорее всего, ошибка. – sbi 14 October 2009 в 17:56
  • 4
    @sbk: Прошу прощения, если я вас обидел, но я не думаю, что 2) это nitpicking. Rvalues ​​просто не const, если вы их не сделаете, и вы можете их изменить, если они не встроены. Мне потребовалось некоторое время, чтобы понять, что, например, пример строки, я делаю что-то неправильно (JFTR: я этого не делаю), поэтому я склонен считать это различие серьезным. – sbi 15 October 2009 в 08:47
  • 5
    Я согласен с sbi - это вопрос совсем не nitpicking. Все это основано на семантике перемещения, что классы классов r сохраняются неконстантно. – Johannes Schaub - litb 15 October 2009 в 11:23

Похоже на исходный вопрос относительно , почему это недопустимо, было ясно дано ответ: «потому что это скорее всего ошибка».

FWIW, я думал, d показать , как это сделать , хотя я не думаю, что это хорошая техника.

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

// Assuming: void Person::GetNameAndAddr(std::string &name, std::string &addr);
string name;
person.GetNameAndAddr(name, string()); // don't care about addr

Как объяснялось в предыдущих ответах, это не компилируется. Но это компилируется и работает корректно (с моим компилятором):

person.GetNameAndAddr(name,
    const_cast<string &>(static_cast<const string &>(string())));

Это просто показывает, что вы можете использовать кастинг, чтобы лгать компилятору. Очевидно, было бы намного проще объявить и передать неиспользуемую автоматическую переменную:

string name;
string unused;
person.GetNameAndAddr(name, unused); // don't care about addr

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

string name;
{
    string unused;
    person.GetNameAndAddr(name, unused); // don't care about addr
}

- Chris

5
ответ дан Chris Pearson 17 August 2018 в 09:35
поделиться

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

Любая ссылка напрямую к временному продлению его жизни [12.2.5]. С другой стороны, ссылка, инициализированная другой ссылкой, будет не (даже если она в конечном счете такая же временная). Это имеет смысл (компилятор не знает, к чему в конечном итоге относится эта ссылка).

Но вся эта идея крайне запутанна. Например. const X &x = X(); сделает временным последним до тех пор, пока ссылка x, но const X &x = X().ref(); не будет (кто знает, что ref() действительно вернул). В последнем случае деструктор для X вызывается в конце этой строки. (Это можно наблюдать с нетривиальным деструктором.)

Таким образом, кажется, что это обычно запутывает и опасно (зачем усложнять правила о жизни объектов?), Но, по-видимому, существует необходимость, по крайней мере, для ссылок на константу, поэтому стандарт устанавливает для них это поведение.

[From sbi comment]: Обратите внимание, что факт, что привязка его к const-ссылке увеличивает временные временные события, является исключением это было добавлено специально (TTBOMK, чтобы обеспечить ручную оптимизацию). Не было добавлено исключение для ссылок, отличных от const, поскольку привязка временной к неконстантной ссылке скорее всего была ошибкой программиста.

Все временные действия сохраняются до тех пор, пока конец полного выражения. Однако, чтобы использовать их, вам нужен трюк, как у вас с ref(). Это законно. Похоже, что лишний обруч не может быть полезен, если не напомнить программисту, что происходит что-то необычное (а именно, контрольный параметр, изменения которого будут быстро потеряны).

[Другой комментарий sbi ]. Причина, по которой Страуступ дает (в D & amp; E) запрет на привязку значений r к неконстантным ссылкам, заключается в том, что если бы Alexey g () изменил бы объект (который вы ожидаете от функции, принимающей неконстантную ссылку), она изменит объект, который умрет, поэтому никто не может получить модифицированное значение в любом случае. Он говорит, что это, скорее всего, ошибка.

16
ответ дан Community 17 August 2018 в 09:35
поделиться
  • 1
    «Таким образом, единственный оставшийся вопрос заключается в том, почему стандарт не хочет, чтобы ссылка на временные ресурсы увеличивала срок службы объекта за пределами конца инструкции» & quot; Вот и все! Вы понимаете мой вопрос. Но я не согласен с вашим мнением. Вы говорите «очень сложно сделать компилятор». но это было сделано для ссылки const. Вы говорите в своем примере «Примечание x является псевдонимом переменной. Какую переменную вы обновляете. & Quot; Нет проблем. Существует уникальная переменная (временная). Некоторый временный объект (равный 5) должен быть изменен. – Alexey Malistov 14 October 2009 в 15:17
  • 2
    @Martin: Висячие ссылки не только неряшливы. Они могут привести к серьезным ошибкам при доступе позже в методе! – mmmmmmmm 14 October 2009 в 15:27
  • 3
    @Alexey: Обратите внимание, что тот факт, что привязка его к const-ссылке увеличивает временные времена жизни, - это исключение , которое было добавлено специально (TTBOMK, чтобы разрешить ручную оптимизацию). Не было добавлено исключение для ссылок, отличных от const, поскольку привязка временной к неконстантной ссылке, скорее всего, была ошибкой программиста. – sbi 14 October 2009 в 18:02
  • 4
    @rstevens: Это было мое мнение ;-) – Martin York 14 October 2009 в 18:57
  • 5
    @alexy: ссылка на невидимую переменную! Не то, что интуитивно. – Martin York 14 October 2009 в 18:59

Основная проблема заключается в том, что

g(getx()); //error

является логической ошибкой: g модифицирует результат getx(), но вы не имеете возможности исследовать измененный объект. Если g не нужно было изменять свой параметр, тогда он не требовал бы ссылки lvalue, он мог бы взять параметр по значению или по ссылке const.

const X& x = getx(); // OK

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

Однако невозможно сделать

X& x = getx(); // error

действительным без делая g(getx()) действительным, что и пытались избежать в первую очередь разработчиками языка.

g(getx().ref()); //OK

справедливо, поскольку методы знают только о константе this, они не не знаю, вызваны ли они lvalue или rvalue.

Как всегда в C ++ у вас есть обходное правило для этого правила, но вы должны сообщить компилятору, что знаете, что вы делаете, будучи явным:

g(const_cast<x&>(getX()));
6
ответ дан Dan Berindei 17 August 2018 в 09:35
поделиться

Зачем вам когда-нибудь хотеть X& x = getx();? Просто используйте X x = getx(); и полагайтесь на RVO.

4
ответ дан DevFred 17 August 2018 в 09:35
поделиться
  • 1
    Потому что я хочу называть g(getx()), а не g(getx().ref()) – Alexey Malistov 14 October 2009 в 12:23
  • 2
    @Alexey, это не настоящая причина. Если вы это сделаете, у вас есть логическая ошибка soemwhere, потому что g собирается изменить то, что вы больше не можете получить. – Johannes Schaub - litb 14 October 2009 в 22:17
  • 3
    @ JohannesSchaub-litb, может быть, ему все равно. – curiousguy 7 December 2011 в 10:18
  • 4
    & quot; полагаться на RVO " за исключением того, что он не называется «RVO». – curiousguy 7 December 2011 в 12:29
  • 5
    @curiousguy: Это приемлемый термин very . Нет ничего плохого в том, чтобы ссылаться на него как «RVO». – Puppy 7 December 2011 в 14:11

«Ясно, что временный объект не является постоянным в примере выше, потому что разрешены вызовы на непостоянные функции. Например, ref () может изменять временный объект.»

In ваш пример getX () не возвращает const X, поэтому вы можете вызвать ref () во многом так же, как вы могли бы назвать X (). ref (). Вы возвращаете non const ref и поэтому можете вызывать non const методы, что вы не можете сделать, это назначить ref для не const-ссылки.

Наряду с комментарием SadSidos это делает ваши три точки неверными.

2
ответ дан Patrick 17 August 2018 в 09:35
поделиться

Зловое обходное решение включает ключевое слово «изменчивое». На самом деле быть злым остается как упражнение для читателя. Или смотрите здесь: http://www.ddj.com/cpp/184403758

4
ответ дан paul 17 August 2018 в 09:35
поделиться

У меня есть сценарий, который я хотел бы рассказать, где бы я хотел сделать то, что спрашивает Алексей. В плагине Maya C ++ я должен сделать следующий синаниган, чтобы получить значение в атрибуте узла:

MFnDoubleArrayData myArrayData;
MObject myArrayObj = myArrayData.create(myArray);   
MPlug myPlug = myNode.findPlug(attributeName);
myPlug.setValue(myArrayObj);

Это утомительно писать, поэтому я написал следующие вспомогательные функции:

MPlug operator | (MFnDependencyNode& node, MObject& attribute){
    MStatus status;
    MPlug returnValue = node.findPlug(attribute, &status);
    return returnValue;
}

void operator << (MPlug& plug, MDoubleArray& doubleArray){
    MStatus status;
    MFnDoubleArrayData doubleArrayData;
    MObject doubleArrayObject = doubleArrayData.create(doubleArray, &status);
    status = plug.setValue(doubleArrayObject);
}

И теперь я могу написать код с начала сообщения как:

(myNode | attributeName) << myArray;

Проблема в том, что он не компилируется вне Visual C ++, потому что он пытается привязать временную переменную, возвращенную из | оператора к ссылке MPlug & lt; оператор. Я бы хотел, чтобы это была ссылка, потому что этот код вызывается много раз, и я бы предпочел не копировать MPlug. Мне нужен только временный объект для жизни до конца второй функции.

Ну, это мой сценарий. Просто подумал, что я приведу пример, где хочется сделать то, что описывает Алексей. Я приветствую все критические замечания и предложения!

Спасибо.

1
ответ дан Robert Trussardi 17 August 2018 в 09:35
поделиться

В вашем коде getx() возвращается временный объект, так называемый «rvalue». Вы можете скопировать rvalues ​​в объекты (aka. Variables) или привязать их к ссылкам const (что продлит их срок службы до конца жизненного цикла ссылки). Вы не можете привязывать значения r к неконстантным ссылкам.

Это было преднамеренное дизайнерское решение, чтобы пользователи случайно не изменяли объект, который умрет в конце выражения:

g(getx()); // g() would modify an object without anyone being able to observe

Если вы хотите сделать это вам придется сначала сделать локальную копию или объект или связать его с ссылкой на const:

X x1 = getx();
const X& x2 = getx(); // extend lifetime of temporary to lifetime of const reference

g(x1); // fine
g(x2); // can't bind a const reference to a non-const reference

Обратите внимание, что следующий стандарт C ++ будет включать ссылки rvalue. То, что вы знаете как ссылки, становится, таким образом, называться «ссылками на lvalue». Вам будет позволено связывать rvalues ​​с rvalue ссылками, и вы можете перегружать функции на «rvalue-ness»:

void g(X&);   // #1, takes an ordinary (lvalue) reference
void g(X&&);  // #2, takes an rvalue reference

X x; 
g(x);      // calls #1
g(getx()); // calls #2
g(X());    // calls #2, too

Идея ссылок на rvalue заключается в том, что, поскольку эти объекты все равно будут умирать, вы могут воспользоваться этими знаниями и реализовать так называемую «семантику перемещения», определенную оптимизацию:

class X {
  X(X&& rhs)
    : pimpl( rhs.pimpl ) // steal rhs' data...
  {
    rhs.pimpl = NULL; // ...and leave it empty, but deconstructible
  }

  data* pimpl; // you would use a smart ptr, of course
};


X x(getx()); // x will steal the rvalue's data, leaving the temporary object empty
29
ответ дан sbi 17 August 2018 в 09:35
поделиться
  • 1
    Привет, это замечательный ответ. Необходимо знать одно: g(getx()) не работает, потому что его подпись g(X& x) и get(x) возвращает временный объект, поэтому мы не можем привязать временный объект ( rvalue ) к не- постоянная ссылка, правильно? И в вашем первом фрагменте кода, я думаю, что это будет const X& x2 = getx(); вместо const X& x1 = getx(); .. – SexyBeast 8 November 2014 в 15:07
  • 2
    Спасибо, что указали на эту ошибку в моем ответе через 5 лет после того, как я ее написал! :-/ Да, ваши рассуждения верны, хотя и немного назад: мы не можем привязывать временные ссылки к ссылкам не const (lvalue), и поэтому временное выражение, возвращаемое getx() (а не get(x)), не может быть привязано к ссылка lvalue - это аргумент g(). – sbi 9 November 2014 в 00:06
  • 3
    Umm, что вы имели в виду под getx() (а не get(x)) ? – SexyBeast 9 November 2014 в 01:13
  • 4
    Когда я пишу & quot; ... getx () (а не get (x)) ... & quot; , я имею в виду, что имя функции - getx(), а не get(x) (as вы написали). – sbi 9 November 2014 в 07:33
  • 5
    О да, конечно. Благодаря.. :) – SexyBeast 9 November 2014 в 21:39

Почему обсуждается в C ++ FAQ (boldfacing mine):

В C ++ ссылки, не относящиеся к const, могут связываться с lvalues и ссылки const могут связываться с lvalues ​​или rvalues, но нет ничего, что могло бы связываться с неконстантным значением r. Это необходимо для защиты людей от изменения значений временных объектов, которые уничтожаются до того, как их новое значение может быть использовано. Например:

void incr(int& a) { ++a; }
int i = 0;
incr(i);    // i becomes 1
incr(0);    // error: 0 is not an lvalue

Если этот incr (0) был разрешен либо временным, что никто никогда не видел, будет увеличиваться или - намного хуже - значение 0 станет 1 . Последнее звучит глупо, но на ранних компиляторах Fortran на самом деле была ошибка, которая выделяла место памяти для хранения значения 0.

7
ответ дан Tony Delroy 17 August 2018 в 09:35
поделиться
  • 1
    Было бы смешно видеть лицо программиста, укушенного этой ошибкой Fortran & quot; ! x * 0 дает x? Какие? Какие?? – John D 15 May 2016 в 02:15
  • 2
    Этот последний аргумент особенно слаб. Ни один компилятор, заслуживающий упоминания, никогда бы не изменил значение от 0 до 1 или даже не интерпретировал incr(0); таким образом. Очевидно, что если бы это было разрешено, это было бы интерпретировано как создание временного целого числа и передача его в incr() – user3204459 22 March 2017 в 15:49
16
ответ дан Community 6 September 2018 в 07:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: