Приветствую, мыщъх, вы писали:
м> printf("var_a = %x\n", a, b++); (хинт: b не предполается выводить на экран, его предполагается увеличить на единицу).
А почему хотябы не "{printf("var_a = %x\n", a); b++;}" ?
Здравствуйте, MikelSV, Вы писали:
MSV>Здравствуйте, alsemm, Вы писали:
A>>В коде случайно не такое: A>>
A>>crbil foo;
A>>foo.add(crbi());
A>>
A>>?
MSV>Да, это первая операция после объявления переменной. MSV>Код: MSV>cl.add(orb.geto(0)); MSV>orb.geto(0) возвращает crbi, так что код в принципе похож.
Тогда так надо переделать, чтобы не ругался:
crbi tmp = orb.geto(0);
cl.add(tmp);
Почему надо переделать? потому что ссылка(&) — это, по сути, тот же указатель (*), т.е. все, что передается по ссылке должно иметь адрес, tmp адрес имеет, а вот временное значение, кот. возвращается из orb.geto(0) адреса не имеет, след. и передаваться по ссылке не может. Константные же ссылки (const &) могут применяться к выражениям/переменным, адрес которых и не взять. Так что надо либо переделывать сигнатуру crbil::add(), либо вводить временную переменную.
Здравствуйте, MikelSV, Вы писали:
MSV>Тогда вопрос посложнее: MSV>
MSV>нет подходящей функции для вызова ‘MSL::SetVal(EVString, MString&, MSLKValLine&)’
MSV>претенденты: bool MSL::SetVal(EVString&, VString, MSLKValLine&)
MSV>bool MSL::SetVal(EVString&, VString)
MSV>Это уже не конструктор. гцц не умеет считать количество параметров?
MSV>Бред крепчает в: MSV>
MSV>ошибка: нет подходящей функции для вызова ‘crbil::add(crbi)’
MSV>280: замечание: претенденты: void crbil::add(crbi&)
MSV>284: замечание: void crbil::add(int, char*)
MSV>288: замечание: void crbil::add(int, char*, int, char*)
MSV>Возможно я не понимаю смысла '&'? MSV>Мне всегда казалось, что & это тоже самое, что и *, только код выглядит как для обычной переменной. MSV>& это Возможности указателя минус сложности работы с указателем.
MSV>Ощущается, что использование const & ограничивает меня в возможностях.
MSV>И не очень понятен смысл фразы "временный объект", ввиду того, что этот объект даже более постоянен.
MSV>Еще один момент, который меня мучает в гцц. MSV>
MSV>Пристает с ошибка: expected ‘,’ or ‘...’ before ‘||’ token MSV>вообще не понятно, чего он хочет.
MSV>В общем куча ошибок, которые вообще не понятно как лечить, и главное не делать снова.
Я б вам посоветовал, во благо в первую очередь тех, кому с вами работать, скачать себе стандарт и почитать на досуге про те моменты, которые вам кажутся "бредом" компилятора. Всего несколько мизерных процентов приходятся на самые настоящие глюки, в том время как большинство проблем возникает по вине самого программиста.
Здравствуйте, Sheridan, Вы писали:
S>Приветствую, мыщъх, вы писали:
м>> printf("var_a = %x\n", a, b++); (хинт: b не предполается выводить на экран, его предполагается увеличить на единицу).
S>А почему хотябы не "{printf("var_a = %x\n", a); b++;}" ?
Я тоже поучаствую в конкурсе:
printf("var_a = %x\n", a), b++;
Хотя printf и получился без побочных эффектов ценой всего-лишь переноса скобки в нужное место, аромат не изменился.
Здравствуйте, wander, Вы писали:
W>Я б вам посоветовал, во благо в первую очередь тех, кому с вами работать, скачать себе стандарт и почитать на досуге про те моменты, которые вам кажутся "бредом" компилятора. Всего несколько мизерных процентов приходятся на самые настоящие глюки, в том время как большинство проблем возникает по вине самого программиста.
дадада, кто написал, тот и виноват.
Вы не поверите:
bool RunFunc(..., Val &retd=(Val&)Val());
ошибка: invalid cast of an rvalue expression of type ‘MCCVal’ to type ‘MCCVal&’
И я пишу в retd. и не интересно, будет ли оно использоваться или разрушится.
Для чего это было сделано история умалчивает, но за такие дела я смотрю на компилятор как на врага.
Стандарты как минимум ограничивают фантазию.
Не могли бы вы дать пример кода для записи адреса функции в переменную для гцц? я потыкался, но ничего хорошего не получилось.
у студии код выглядит так: void*f=write; или void*f=print. один вариант для всех функций.
Также есть подозрение, что гцц неправильно относится к слову timeout.
virtual int timeout(){ ... return 0; }
ошибка: ‘stdscr’ is not a type
ошибка: expected identifier before ‘)’ token
Ругается на совпадение названия класса и функции??
ошибка: declaration of ‘bool Listen::SendData(char*, unsigned int, fdata&)’
ошибка: changes meaning of ‘SendData’ from ‘class SendData’
А читать стандарт, это как читать книгу про устройство мира, вместо того, чтобы посмотреть, как же оно на самом деле устроено.
Римское правило. Тот, кто говорит, что Это не может быть сделано, никогда не должен мешать тому, кто Это делает.
Осень, ну вы поняли.
Зачем еще один код? А человек?
Здравствуйте, MikelSV, Вы писали:
MSV>дадада, кто написал, тот и виноват.
MSV>Вы не поверите: MSV>bool RunFunc(..., Val &retd=(Val&)Val()); MSV>ошибка: invalid cast of an rvalue expression of type ‘MCCVal’ to type ‘MCCVal&’
Поверим.
MSV>Не могли бы вы дать пример кода для записи адреса функции в переменную для гцц? я потыкался, но ничего хорошего не получилось. MSV>у студии код выглядит так: void*f=write; или void*f=print. один вариант для всех функций.
reinterpret_cast<long>
MSV>Также есть подозрение, что гцц неправильно относится к слову timeout. MSV>virtual int timeout(){ ... return 0; } MSV>ошибка: ‘stdscr’ is not a type MSV>ошибка: expected identifier before ‘)’ token
curses.h:
#define timeout(delay) wtimeout(stdscr,delay)
При чем здесь компилятор? В Windows.h на макрос min не напарывались?
MSV>Ругается на совпадение названия класса и функции?? MSV>ошибка: declaration of ‘bool Listen::SendData(char*, unsigned int, fdata&)’ MSV>ошибка: changes meaning of ‘SendData’ from ‘class SendData’
Код целиком?
MSV>А читать стандарт, это как читать книгу про устройство мира, вместо того, чтобы посмотреть, как же оно на самом деле устроено.
Подозреваю, что если бы вы с таким подходом писали изначально под gcc, а потом перешли на MSVC, вы столкнулись бы с кучей похожих проблем. "На самом деле устроено" оно в каждом компиляторе по-разному. Для этого стандарт и нужен.
Здравствуйте, MikelSV, Вы писали:
MSV>... MSV>Не могли бы вы дать пример кода для записи адреса функции в переменную для гцц? я потыкался, но ничего хорошего не получилось. MSV>у студии код выглядит так: void*f=write; или void*f=print. один вариант для всех функций.
Это неправильно. В C строго говоря нельзя конвертировать указатели на функции в указатели другого типа (см. стандарт, лень искать ссылку).
В C++ указатели на разные функции (методы) могут иметь разный размер в разных случаях под одним компилятором на той же OS.
Можно записывать указатель на функцию в переменную такого же типа.
И вообще — поменьше эмоций. Как правило вопли о том, какой gcc плохой и какой msvc хороший идут от тех, кто мало знаком с первым.
Здравствуйте, Fwiffo, Вы писали:
MSV>>Не могли бы вы дать пример кода для записи адреса функции в переменную для гцц? я потыкался, но ничего хорошего не получилось. MSV>>у студии код выглядит так: void*f=write; или void*f=print. один вариант для всех функций.
F>
F>reinterpret_cast<long>
F>
Вот не понял мысли. как этим пользоваться? Можно более привычного вида? типа void*f...
этот код в линуксе пока не будет использоваться, но как минимум должна сохраняться работа в windows.
6 ошибок до канадской границы. 3 штуки: 2+2+2 с указателем на функцию.
Изменение имени класса на SendDataS помогло. однако интересный юмор.
Подскажите и я уже запущу эту фигню.
А учитывая, что там идет работа с терминалом, сетью и прочими делами, запуск будет очень интересным.
F>Подозреваю, что если бы вы с таким подходом писали изначально под gcc, а потом перешли на MSVC, вы столкнулись бы с кучей похожих проблем. "На самом деле устроено" оно в каждом компиляторе по-разному. Для этого стандарт и нужен.
Не думаю. разве что проблемы с макросами. Если студия более легко относится к коду, то проблем именно с самим кодом будет мало.
A>Это неправильно. В C строго говоря нельзя конвертировать указатели на функции в указатели другого типа (см. стандарт, лень искать ссылку). A>В C++ указатели на разные функции (методы) могут иметь разный размер в разных случаях под одним компилятором на той же OS.
Это какие же случаи??
Мне нужен лишь указатель на функцию, 4 байта для 32бит. Просто дайте мне его получить.
A>И вообще — поменьше эмоций. Как правило вопли о том, какой gcc плохой и какой msvc хороший идут от тех, кто мало знаком с первым.
Да, кто знаком хорошо эмоций выдают мало, смирились.
Гцц нормальный, но долбанутый. Я в общем-то сильно не ругаю, потому как уважаю.
А в студии действительно все намного проще. О студии я помню, что писал в ней код, о гцц, что пытался отладить.
Я был бы просто счастлив, если бы в гцц можно было нормально писать программы. но я не знаю таких средств разработки. может они есть, а я не знаю и до сих пор пишу код в mc?
Римское правило. Тот, кто говорит, что Это не может быть сделано, никогда не должен мешать тому, кто Это делает.
Осень, ну вы поняли.
Зачем еще один код? А человек?
Здравствуйте, MikelSV, Вы писали:
MSV>Вот не понял мысли. как этим пользоваться? Можно более привычного вида? типа void*f... MSV>этот код в линуксе пока не будет использоваться, но как минимум должна сохраняться работа в windows.
Имелось ввиду явно кастить к нужному типу:
void* f = reinterpret_cast<void*> ( someFunc );
MSV>Это какие же случаи??
Когда берётся указатель на виртуальную функцию-член, например. Вот на моём компиляторе (32-битном) уже не 4 байта, а все 8
MSV>Изменение имени класса на SendDataS помогло. однако интересный юмор. MSV>Подскажите и я уже запущу эту фигню.
Про SendData больно мало информации.
MSV>Гцц нормальный, но долбанутый.
Интересно, как это можно совмещать?
Право, эту точку зрения я не разделяю.
MSV>А в студии действительно все намного проще. О студии я помню, что писал в ней код, о гцц, что пытался отладить. MSV>Я был бы просто счастлив, если бы в гцц можно было нормально писать программы. но я не знаю таких средств разработки. может они есть, а я не знаю и до сих пор пишу код в mc?
Вы спрашиваете об IDE, или я что-то не так понял?
Если да, то лично я сейчас пересел на Eclipse + CDT (когда надо что-то быстро проверить, пинаю Code::Blocks — только из-за того, что он быстрее запускается) — посмотрите в эту сторону. Да, если захотите попробовать, то не забудьте пролистать справку — я по первой так не сделал и мучился, не замечая нужные фичи
Здравствуйте, Alexey F, Вы писали:
AF>Здравствуйте, MikelSV, Вы писали:
MSV>>Вот не понял мысли. как этим пользоваться? Можно более привычного вида? типа void*f... MSV>>этот код в линуксе пока не будет использоваться, но как минимум должна сохраняться работа в windows. AF>Имелось ввиду явно кастить к нужному типу: AF>
AF>void* f = reinterpret_cast<void*> ( someFunc );
AF>
Нет, имелось в виду именно к числовому типу. Пункт относительно преобразования указателя на функцию в void* появится в следующем стандарте. Comeau это компилирует только начиная с 4.3.10.1 Beta2.
void (*p)();
void* v = reinterpret_cast<void*> ( p ); // error: invalid type conversionlong l = reinterpret_cast<long> ( p ); // Ok
Здравствуйте, MikelSV, Вы писали:
MSV>В то время, когда корабли^W студия собирает проект, g++ упорно придирается к разным моментам. MSV>Например:
MSV>
MSV>ошибка: нет подходящей функции для вызова ‘EVString::EVString(EVString)’
MSV>замечание: претенденты: EVString::EVString(EVString&)
MSV>замечание: EVString::EVString(VString)
MSV>
MSV>Разобрался, что EVString::EVString(const EVString&) спасает положение. Но с другой стороны это бред, почему я не могу менять данные?
MSV>не получается: MSV> ошибка: некорректное преобразование из ‘int (*)(int, int)’ в ‘void*’ MSV>А всего то хотелось получить адрес функции.
MSV>И так далее. В основном ошибки, связанные с переводом из одного типа в другой. MSV>Долгая отладка таких, казалось бы простых вещей, которые понимает даже студия (2003 года), создают мнение о гцц, как о тупом компиляторе. MSV>Я так понимаю есть веские основания для всех этих глюков?
Здравствуйте, Fwiffo, Вы писали:
F>Нет, имелось в виду именно к числовому типу. Пункт относительно преобразования указателя на функцию в void* появится в следующем стандарте. Comeau это компилирует только начиная с 4.3.10.1 Beta2. F>
F>void (*p)();
F>void* v = reinterpret_cast<void*> ( p ); // error: invalid type conversion
F>long l = reinterpret_cast<long> ( p ); // Ok
F>
Вот спасибо! А то я всё время до этого думал, что к void* они тоже кастятся.
Топикстартер может всё равно этим воспользоваться (на свой страх и риск: если нет возможности переделать): в принципе, т.к. gcc нормально отнёсся к такому коду, что и ввело в заблуждение меня
Здравствуйте, MikelSV, Вы писали:
MSV>Вы не поверите: MSV>bool RunFunc(..., Val &retd=(Val&)Val()); MSV>ошибка: invalid cast of an rvalue expression of type ‘MCCVal’ to type ‘MCCVal&’
Отчего же, поверю. Это элементарно.
bool RunFunc(..., Val const & retd = Val());
Такие вещи подразумевают константность временных данных. Это очень редкий случай, когда действительно нужно менять что-то в таких объектах. В первую очередь, если возникает такой гвоздь — причина пересмотреть дизайн программы.
MSV>И я пишу в retd. и не интересно, будет ли оно использоваться или разрушится.
Как исправить — написано выше. Если вам нужно нарушить закон, вы должны будете отвечать за последствия. Нарушать закон можно так:
bool RunFunc(..., Val const & retd = Val())
{
Val & retd_ = const_cast<Val &>(retd);
}
MSV>Для чего это было сделано история умалчивает
(8.5.3/5) MSV>но за такие дела я смотрю на компилятор как на врага.
Смотрите лучше так на того, кто это писал.
MSV>Стандарты как минимум ограничивают фантазию.
Стандарты обеспечивают порядок. И не вина GCC, что MSVC выбрал путь анархии.
Здравствуйте, wander, Вы писали:
MSV>>но за такие дела я смотрю на компилятор как на врага. W>Смотрите лучше так на того, кто это писал.
Не хочу так смотреть на себя.
MSV>>Стандарты как минимум ограничивают фантазию. W>Стандарты обеспечивают порядок. И не вина GCC, что MSVC выбрал путь анархии.
Не чувствую, что сохранение порядка главная причина.
Программа запускается, но это сохранение порядка толкает на создание кривого кода.
Проблема все с теми же:
ошибка: вызов перегруженной ‘MString(HLString&)’ имеет неоднозначную трактовку
претенденты: MString::MString(const VString&)
MString::MString(const MString&)
MString::MString(unsigned int) <near match>
...
HLString : public LString
в LString есть operator MString().
---
теперь вызывается с (MString&)lsret; но это как вы можете увидеть явный вылет, зато гцц не ругается.
operator MString() -> MString& -> неверный указатель на память.
Вызов при котором даже в студия вылетает, что естественно:
Drawn((MString&)(HLString()+"Uncnown command '"+comm+"'\r\n\r\n"));
При:
void Drawn(const MString &vs);
Нормальный код, без '&', гцц не принимает, вот не может понять, во что преобразовать. .
Drawn((MString)(HLString()+"Uncnown command '"+comm+"'\r\n\r\n"));
Чем здесь можно помочь компилятору?
Римское правило. Тот, кто говорит, что Это не может быть сделано, никогда не должен мешать тому, кто Это делает.
Осень, ну вы поняли.
Зачем еще один код? А человек?
Здравствуйте, MikelSV, Вы писали:
MSV>Здравствуйте, wander, Вы писали:
MSV>>>но за такие дела я смотрю на компилятор как на врага. W>>Смотрите лучше так на того, кто это писал. MSV>Не хочу так смотреть на себя.
MSV>>>Стандарты как минимум ограничивают фантазию. W>>Стандарты обеспечивают порядок. И не вина GCC, что MSVC выбрал путь анархии. MSV>Не чувствую, что сохранение порядка главная причина.
MSV>Программа запускается, но это сохранение порядка толкает на создание кривого кода.
Верите вы или нет, но качество кода в первую очередь зависит от того как именно написана программа. Если в программе постоянно используются такого рода хаки (как с конст_каст например в примере выше), то рано или поздно это случится (вылет). И не важно что у вас за компилятор. Поэтому я и говорю — когда возникают такого рода проблемы — нужно пересматривать дизайн.
MSV>Проблема все с теми же: MSV> ошибка: вызов перегруженной ‘MString(HLString&)’ имеет неоднозначную трактовку MSV> претенденты: MString::MString(const VString&) MSV> MString::MString(const MString&) MSV> MString::MString(unsigned int) <near match> MSV> ...
MSV>HLString : public LString MSV>в LString есть operator MString().
MSV>--- MSV>теперь вызывается с (MString&)lsret; но это как вы можете увидеть явный вылет, зато гцц не ругается. MSV>operator MString() -> MString& -> неверный указатель на память.
Покажите хотя бы кусок цельного кода. Так не ясно что вы пытаетесь сотворить.
MSV>Вызов при котором даже в студия вылетает, что естественно: MSV>Drawn((MString&)(HLString()+"Uncnown command '"+comm+"'\r\n\r\n"));
Здравствуйте, MikelSV, Вы писали:
MSV>Здравствуйте, wander, Вы писали:
MSV>>>но за такие дела я смотрю на компилятор как на врага. W>>Смотрите лучше так на того, кто это писал. MSV>Не хочу так смотреть на себя.
А вот надо. Самокритика в нашей профессии — верный путь к вершинам профессионализма.
Здравствуйте, wander, Вы писали:
MSV>>HLString : public LString MSV>>в LString есть operator MString().
MSV>>--- MSV>>теперь вызывается с (MString&)lsret; но это как вы можете увидеть явный вылет, зато гцц не ругается. MSV>>operator MString() -> MString& -> неверный указатель на память.
W>Покажите хотя бы кусок цельного кода. Так не ясно что вы пытаетесь сотворить.
MSV>>Вызов при котором даже в студия вылетает, что естественно: MSV>>Drawn((MString&)(HLString()+"Uncnown command '"+comm+"'\r\n\r\n"));
W>Покажите как operator+ реализован.
Кода много, не знаю, что именно показывать.
В данном случае единственный вариант реализации operator+ может быть:
HLString& operator+(const MString& string){...}
Это строковый класс, он прибавляет текст у уже записанному в нем.
Собственно тут все просто, класс собирает текст в строку и возвращает ее через operator MString().
Кстати этот код работает:
Drawn((HLString()+"Uncnown command '"+comm+"'\r\n\r\n").operator MString());
Но я за то, чтобы компилятор сам догадывался о типах и не нужно было дописывать .operator MString().
В Drawn текст ждут в виде const MString&.
Все просто.
В случае:
Drawn((MString)(HLString()+"Uncnown command '"+comm+"'\r\n\r\n"));
гцц не понимает указания.
По идее главная ошибка в том, что гцц не понимает, в каком типе передавать из HLString& в MString.
У HLString есть:
operator MString();
operator char*();
Этакая слепота. Ну не могу поверить, что он не может понять, что нужно передать из HLString& 'operator MString()' в MString 'MString(const MString&)'.
Варианты для гцц также мучил в студии. Все нормально понимаются. Жесткое следование стандартам — зло.
Римское правило. Тот, кто говорит, что Это не может быть сделано, никогда не должен мешать тому, кто Это делает.
Осень, ну вы поняли.
Зачем еще один код? А человек?
Здравствуйте, MikelSV, Вы писали:
MSV>Кода много, не знаю, что именно показывать.
MSV>В данном случае единственный вариант реализации operator+ может быть: MSV>HLString& operator+(const MString& string){...} Это далеко не единственный вариант ошибочной реализации operator+ и подобных ему операторов. MSV>Это строковый класс, он прибавляет текст у уже записанному в нем.
Суть operator+ в создании нового объекта посредством объединения двух имеющихся (конкатенации).
Возьмем например:
a = 2 + 2
Было бы маразмом полагать, что первое слагаемое после этого станет равным 4. Это две константы. Так какого, простите, перепуга вы ожидаете другого поведения с вашими строками? Если вы пишете
some_class1(5) + some_class2(4)
— это тоже самое. Поэтому, во-первых, operator+, operator- и иже с ними никогда не должны возвращать ссылку, если только вы не хотите устроить себе грабли на ровном месте. во-вторых, семантика описанного вами оператора соответствует operator+=, вот он, да, он возвращает ссылку, только это совсем другая песня. в-третьих, функция operator+ должна быть свободной. Во избежание неожиданных эксцессов в вариантах с перестановкой слагаемых.
Итого:
my_string operator+(my_string const & a, other_string const & b) // const, ибо мы не изменяем слагаемые, мы формируем из них результат
{
my_string result(a.size() + b.size());
// операции по объединению строкreturn result; // в результате my_string, ибо мы прибавляем к ней
}
other_string operator+(other_string const & a, my_string const & b) // const, ибо мы не изменяем слагаемые, мы формируем из них результат
{
other_string result(a.size() + b.size());
// операции по объединению строкreturn result; // в результате other_string, ибо мы прибавляем к ней
}
other_string const & res = other_string("bla-bla") + my_string("bla-bla"); // будет работать
other_string res = other_string("bla-bla") + my_string("bla-bla"); // будет работать
my_string const & res = my_string("bla-bla") + other_string("bla-bla"); // ок
my_string res = my_string("bla-bla") + other_string("bla-bla"); // ок
my_string & res = my_string("bla-bla") + other_string("bla-bla"); // бамс, ошибка!
Я заметил, что вы пропускаете мимо ушей все что я пишу. Поэтому советую вам обратиться в главу 11 — Перегрузка Операторов, русского издания книги Б. Страуструпа "Язык программирование С++. 3е издание", для вдумчивого чтения и подтверждения всего, что я выше написал.
MSV>Собственно тут все просто, класс собирает текст в строку и возвращает ее через operator MString().
Вы правы, все очень просто. Можно например посмотреть реализацию такого же механизма в библиотеке QT. Добавлю, что если придерживаться правил, которые я описал выше оператор приведения вам и вовсе не понадобится.
MSV>Кстати этот код работает: MSV>Drawn((HLString()+"Uncnown command '"+comm+"'\r\n\r\n").operator MString()); MSV>Но я за то, чтобы компилятор сам догадывался о типах и не нужно было дописывать .operator MString().
Теперь к вашим баранам, откройте для себя mutable:
MSV>Варианты для гцц также мучил в студии. Все нормально понимаются. Жесткое следование стандартам — зло.
Зло — это нарушение стандартов студией. Если бы язык был в ней правильно реализован с самого начала, то всего этого бардака не было бы.
Ведь все это сделано не просто так, а потому что иначе — нелогично. Пример с оператором+ ясно это показывает.
Странно, что приходится объяснять это, в двадцать первом-то веке.
Когда в C++ появились ссылки, код типа этого:
void f(int& c);
f(42);
был допустим. Создавалось временное значение, и ссылка привязывалась к нему. Целью этого было, надо полагать, единообразие; а если тебя интересует измененный объект, так передавай именованное значение, а не временное.
Очень скоро стало ясно, что эта фича есть багогенератор, например:
void increment(int& x)
{
++x;
cout << x << endl;
}
int i = 42;
increment(i); // 43
cout << i << endl; // 43double d = 42;
increment(d); // 43
cout << d << endl; // 42
потому что изменения применялись только ко временному объекту. Даже Borland® C++™ 3.1 выдает warning на такие конструкции.
Поэтому C++98 разрешает привязку ссылок к rvalue только для константных типов. Впрочем, старый подход всё же позволял делать некоторые полезные вещи. Например, деструктивные операции, когда конечное состояние объекта неважно. Поэтому в C++0x есть третий тип ссылок — rvalue references, которые действуют, как достандартные неконстантные ссылки. Итак, X& и X&& позволяют применять к объекту неконстантные операции, X const& и X&& привязываются ко временным объектам.
За подробностями — в стандарт, в «Design and Evolution of C++» и в любимый поисковик.