Здравствуйте, wayfaring, Вы писали:
W>Легален ли следующий код: W>...
Вот о чем подумалось. С появлением rvalue references появился способ обрабатывать временные объекты отдельно от остальных. И теперь можно защищать подобные функции от опасного использования, например так:
Перегрузка, принимающая аргумент как rvalue reference, возвращает результат по значению. Благодаря этому использование function становится безопасным в любых вариантах:
std::string text = "Hello, World!";
const std::string& s1 = function(text);
const std::string& s2 = function("Happy New Year!"); //OK!
Хотя в некоторых случаях такой прием будет приводить к нежелательному копированию, например:
я же написал — что это легально, но к примеру у меня в привычках есть принимать возвращаемое значение-объект в конст-ссылку, и подозреваю не у меня одного, так что вышеописанный код — западло, но в определенных случаях (жесткая оптимизация) может быть оправдано
Как много веселых ребят, и все делают велосипед...
Здравствуйте, ononim, Вы писали:
O>я же написал — что это легально, но к примеру у меня в привычках есть принимать возвращаемое значение-объект в конст-ссылку, и подозреваю не у меня одного, так что вышеописанный код — западло, но в определенных случаях (жесткая оптимизация) может быть оправдано
Странная привычка. Экономия на cctor? Так ведь компилятор умеет экономить сам, выполняя RVO.
Код с протягиванием ссылки от параметра до возвращаемого значения может встречаться в разных expression template.
И задача его — не жёсткая оптимизация, а преобразование типов, либо просто терпимость к типам, которые noncopyable формально или по смыслу (тяжеловесные данные).
В отношении std::string это, конечно, выглядит пижонством и преждевременной оптимизацией.
ononim:
O>я же написал — что это легально, но к примеру у меня в привычках есть принимать возвращаемое значение-объект в конст-ссылку, и подозреваю не у меня одного
Зачем так делать? Чем
Result f();
Result const &result = f();
лучше по сравнению с
Result f();
Result const result = f();
?
Правилами C++03 разрешалось создание любого конечного количества копий в обоих случаях. Правила C++11 требуют, чтобы ссылка result связывалась с исходным временным объектом непосредственно (без создания дополнительных копий), но очевидно, что для этого требуется тот же механизм, который можно использовать для RVO. Проверка доступности copy/move конструктора выполняется в контексте return statement вызываемой функции (инициализация ссылки снаружи функции эту проверку не устраняет). Любой вменяемый компилятор в обоих случаях поступит одинаково.
O>так что вышеописанный код — западло
Я так не считаю. Можно и при использовании возврата по значению накосячить:
Не могли бы вы объяснить, чем приведенный вами код будет "летален"?
Вы создаете временный неименованный объект, на который указывает константная ссылка. И пока ссулка будет жива, временный объект, на который сцществует константная сслыка, также будет жить, пока ссылка не выйдет за пределы области своей видимости.
И чем ваш код отличается, например, от следующего кода
С>Не могли бы вы объяснить, чем приведенный вами код будет "летален"? С>Вы создаете временный неименованный объект, на который указывает константная ссылка. И пока ссулка будет жива, временный объект, на который сцществует константная сслыка, также будет жить, пока ссылка не выйдет за пределы области своей видимости.
Тем, что, в общем случае, у компилятора нет информации о том, что функция возвращает ссылку именно на тот объект, который был передан функции в качестве аргумента. Следовательно, и оснований для продления времени жизни этого объекта у компилятора нет. Ну разжевывали же уже эту ситуацию, даже не один раз ЕМНИП.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, Сыроежка, Вы писали:
С>>Здравствуйте, ononim, Вы писали:
W>>>>Легален ли следующий код: O>>>легален, но будет летален в таком случае: O>>>
С>>Не могли бы вы объяснить, чем приведенный вами код будет "летален"? С>>Вы создаете временный неименованный объект, на который указывает константная ссылка. И пока ссулка будет жива, временный объект, на который сцществует константная сслыка, также будет жить, пока ссылка не выйдет за пределы области своей видимости.
R>Тем, что, в общем случае, у компилятора нет информации о том, что функция возвращает ссылку именно на тот объект, который был передан функции в качестве аргумента. Следовательно, и оснований для продления времени жизни этого объекта у компилятора нет. Ну разжевывали же уже эту ситуацию, даже не один раз ЕМНИП.
Любопытно, почему у компилятора нет такой информации?! А на какой тогда по вашему объект возвращается ссылка?!
С>>>Не могли бы вы объяснить, чем приведенный вами код будет "летален"? С>>>Вы создаете временный неименованный объект, на который указывает константная ссылка. И пока ссулка будет жива, временный объект, на который сцществует константная сслыка, также будет жить, пока ссылка не выйдет за пределы области своей видимости.
R>>Тем, что, в общем случае, у компилятора нет информации о том, что функция возвращает ссылку именно на тот объект, который был передан функции в качестве аргумента. Следовательно, и оснований для продления времени жизни этого объекта у компилятора нет. Ну разжевывали же уже эту ситуацию, даже не один раз ЕМНИП.
С>Любопытно, почему у компилятора нет такой информации?! А на какой тогда по вашему объект возвращается ссылка?!
По принципу раздельной компиляции — эта функция может быть определена в другой единице трансляции. И внутреннее представление ссылок сравнить компилятор не может, потому, что во время компиляции их просто нет.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, Сыроежка, Вы писали:
W>>>>>>Легален ли следующий код: O>>>>>легален, но будет летален в таком случае: O>>>>>
С>>>>Не могли бы вы объяснить, чем приведенный вами код будет "летален"? С>>>>Вы создаете временный неименованный объект, на который указывает константная ссылка. И пока ссулка будет жива, временный объект, на который сцществует константная сслыка, также будет жить, пока ссылка не выйдет за пределы области своей видимости.
R>>>Тем, что, в общем случае, у компилятора нет информации о том, что функция возвращает ссылку именно на тот объект, который был передан функции в качестве аргумента. Следовательно, и оснований для продления времени жизни этого объекта у компилятора нет. Ну разжевывали же уже эту ситуацию, даже не один раз ЕМНИП.
С>>Любопытно, почему у компилятора нет такой информации?! А на какой тогда по вашему объект возвращается ссылка?!
R>По принципу раздельной компиляции — эта функция может быть определена в другой единице трансляции. И внутреннее представление ссылок сравнить компилятор не может, потому, что во время компиляции их просто нет.
Честно признаюсь, не понял, какое значение имеет раздельная компиляция? При вызове этой функции (не важно, где она определена) создается неименованный объъект, и создается ссылка на этот объект. Далее определение функции знает, что она получила ссылку и эту ссылку возвращает. То есть внутри функции никакие новые ссылки не создаются. Поэтому не вижу причем, почему раздельная компиляции как-то должна повлиять. Если бы это было так, то можно было бы сказать, что половина кода на С++ некоректные. Ведь как происходит удаление временных объектов? Компилятор смотрит, есть ли на него ссылки (не важно, сколько этих ссылок), и если нет, то удаляет объект. То же самое происходит и в этом конкретном случае. После вызова функции компилятор смотрит, есть ли ссылки на объект (а они есть, причем, как вы считаете, их может быть даже две, так как по вашему из-за раздельной компиляции компилятор не знает, что эти две ссылки одни и те же), и если есть, то объяект не удаляетася. И этот механизм не влияет на наличие раздельной компиляции.
С>>>>>Не могли бы вы объяснить, чем приведенный вами код будет "летален"? С>>>>>Вы создаете временный неименованный объект, на который указывает константная ссылка. И пока ссулка будет жива, временный объект, на который сцществует константная сслыка, также будет жить, пока ссылка не выйдет за пределы области своей видимости.
R>>>>Тем, что, в общем случае, у компилятора нет информации о том, что функция возвращает ссылку именно на тот объект, который был передан функции в качестве аргумента. Следовательно, и оснований для продления времени жизни этого объекта у компилятора нет. Ну разжевывали же уже эту ситуацию, даже не один раз ЕМНИП.
С>>>Любопытно, почему у компилятора нет такой информации?! А на какой тогда по вашему объект возвращается ссылка?!
R>>По принципу раздельной компиляции — эта функция может быть определена в другой единице трансляции. И внутреннее представление ссылок сравнить компилятор не может, потому, что во время компиляции их просто нет.
С>Честно признаюсь, не понял, какое значение имеет раздельная компиляция? При вызове этой функции (не важно, где она определена) создается неименованный объъект, и создается ссылка на этот объект. Далее определение функции знает, что она получила ссылку и эту ссылку возвращает. То есть внутри функции никакие новые ссылки не создаются. Поэтому не вижу причем, почему раздельная компиляции как-то должна повлиять. Если бы это было так, то можно было бы сказать, что половина кода на С++ некоректные. Ведь как происходит удаление временных объектов? Компилятор смотрит, есть ли на него ссылки (не важно, сколько этих ссылок), и если нет, то удаляет объект. То же самое происходит и в этом конкретном случае. После вызова функции компилятор смотрит, есть ли ссылки на объект (а они есть, причем, как вы считаете, их может быть даже две, так как по вашему из-за раздельной компиляции компилятор не знает, что эти две ссылки одни и те же), и если есть, то объяект не удаляетася. И этот механизм не влияет на наличие раздельной компиляции.
не знаю о каком С++ ты говоришь, но в том си, который я знаю, компилятор не считает никаких ссылок на временные объекты, а честно удаляет созданные объекты в конце выражения.
что нетрудно проверить:
struct test {
test () { std::cout << __PRETTY_FUNCTION__ << std::endl; }
test ( test const & ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
~test () { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
test const & foo ( test const & x ) { return x; }
int main () {
test const & ref = foo( test() );
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Здравствуйте, Сыроежка, Вы писали:
С>Здравствуйте, rg45, Вы писали:
R>>Здравствуйте, Сыроежка, Вы писали:
W>>>>>>>Легален ли следующий код: O>>>>>>легален, но будет летален в таком случае: O>>>>>>
С>>>>>Не могли бы вы объяснить, чем приведенный вами код будет "летален"? С>>>>>Вы создаете временный неименованный объект, на который указывает константная ссылка. И пока ссулка будет жива, временный объект, на который сцществует константная сслыка, также будет жить, пока ссылка не выйдет за пределы области своей видимости.
R>>>>Тем, что, в общем случае, у компилятора нет информации о том, что функция возвращает ссылку именно на тот объект, который был передан функции в качестве аргумента. Следовательно, и оснований для продления времени жизни этого объекта у компилятора нет. Ну разжевывали же уже эту ситуацию, даже не один раз ЕМНИП.
С>>>Любопытно, почему у компилятора нет такой информации?! А на какой тогда по вашему объект возвращается ссылка?!
R>>По принципу раздельной компиляции — эта функция может быть определена в другой единице трансляции. И внутреннее представление ссылок сравнить компилятор не может, потому, что во время компиляции их просто нет.
С>Честно признаюсь, не понял, какое значение имеет раздельная компиляция? При вызове этой функции (не важно, где она определена) создается неименованный объъект, и создается ссылка на этот объект. Далее определение функции знает, что она получила ссылку и эту ссылку возвращает. То есть внутри функции никакие новые ссылки не создаются. Поэтому не вижу причем, почему раздельная компиляции как-то должна повлиять. Если бы это было так, то можно было бы сказать, что половина кода на С++ некоректные. Ведь как происходит удаление временных объектов? Компилятор смотрит, есть ли на него ссылки (не важно, сколько этих ссылок), и если нет, то удаляет объект. То же самое происходит и в этом конкретном случае. После вызова функции компилятор смотрит, есть ли ссылки на объект (а они есть, причем, как вы считаете, их может быть даже две, так как по вашему из-за раздельной компиляции компилятор не знает, что эти две ссылки одни и те же), и если есть, то объяект не удаляетася. И этот механизм не влияет на наличие раздельной компиляции.
Ну хорошо, давай рассмотрим пример:
#include <string>
#include <iostream>
const std::string& foo(const std::string&);
int main()
{
const std::string& text = foo("bla-bla");
std::cout << text << std::endl;
}
Код вполне wel-formed и компилятор его успешно компилирует. Вопрос: по каким признакам компилятор должен понять, что временному объекту, созданному для передачи в функцию foo, нужно продлить время жизни?
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Masterkent, Вы писали:
M>Зачем так делать? Чем
M>
Result f();
M>Result const &result = f();
M>лучше по сравнению с
M>
Result f();
M>Result const result = f();
M>?
В случае если сигнатура f изменится на
Result const & f();
(что бывает, например, когда f() член класса и значение сохраняется для использования в других функциях), вызывающий код не придется модифицировать чтобы он принимал возвращаемое значение по ссылке а не по значению.
H>(что бывает, например, когда f() член класса и значение сохраняется для использования в других функциях), вызывающий код не придется модифицировать чтобы он принимал возвращаемое значение по ссылке а не по значению.
Сомнительный аргумент. Таким невинным с виду изменением интерфейса можно и чей-нибудь код сломать:
class X
{
public:
Result f();
....
};
void foo(X &x)
{
Result const &result = x.f();
modify(x); // после этой строчки x.f() вернуло бы другой результат
use(result); // используем старое значение x.f()
}
class X
{
public:
Result const &f();
....
};
void foo(X &x)
{
Result const &result = x.f();
modify(x); // после этой строчки значение по ссылке result изменилось
use(result); // хотели использовать старое значение x.f(), а получили использование модифицированного объекта
}
Здравствуйте, Masterkent, Вы писали:
M>hmich:
M>Сомнительный аргумент. Таким невинным с виду изменением интерфейса можно и чей-нибудь код сломать:
M>
class X
M>{
M>public:
M> Result const &f();
M> ....
M>};
M>void foo(X &x)
M>{
M> Result const &result = x.f();
M> modify(x); // после этой строчки значение по ссылке result изменилось
M> use(result); // хотели использовать старое значение x.f(), а получили использование модифицированного объекта
M>}
Не спорю, этот прием имеет смысл только при работе с константными объектами.
class X1
{
public:
Result const &f() const;
....
};
class X2
{
public:
Result f() const;
....
};
template < class X > void foo(X const &x)
{
Result const &result = x.f();
....
}
В этом случае foo работает с одинаковой производительностью и с X1, и с X2. Если мы принимаем результат функции по значению, то в случае с X1 мы получаем лишнее копирование.