В чем разница между этими двумя версиями одного и того же класса шаблонов [duplicate]

Типы ссылок по умолчанию равны null, чтобы указать, что они не ссылаются на какой-либо объект. Следовательно, если вы попытаетесь получить доступ к объекту, на который ссылаетесь, а его нет, вы получите исключение NullReferenceException.

Для Ex:

SqlConnection connection = null;
connection.Open();

Когда вы запускаете это кода, вы получите:

System.NullReferenceException: Object reference not set to an instance of an object.

Вы можете избежать этой ошибки, например, следующим образом:

if (connection != null){
    connection.Open();
}

Примечание. Чтобы избежать этой ошибки, вы всегда должны инициализировать свои объекты прежде чем пытаться что-либо сделать с ними.

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

10 ответов

Из этой статьи в блоге 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 28 August 2018 в 02:25
поделиться

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

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 28 August 2018 в 02:25
поделиться

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

Любая ссылка напрямую к временному продлению его жизни [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 28 August 2018 в 02:25
поделиться

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

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 28 August 2018 в 02:25
поделиться

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

4
ответ дан DevFred 28 August 2018 в 02:25
поделиться

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

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

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

2
ответ дан Patrick 28 August 2018 в 02:25
поделиться

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

4
ответ дан paul 28 August 2018 в 02:25
поделиться

У меня есть сценарий, который я хотел бы рассказать, где бы я хотел сделать то, что спрашивает Алексей. В плагине 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 28 August 2018 в 02:25
поделиться

В вашем коде 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 28 August 2018 в 02:25
поделиться

Почему обсуждается в 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 28 August 2018 в 02:25
поделиться
Другие вопросы по тегам:

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