- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
//Сом ненужный щит
#include <iostream>
using namespace std;
#include <string>
#include <iostream>
struct Tracer {
Tracer(void)
:m_name("(none)")
{
std::cout << "[" << m_name << "] " << __PRETTY_FUNCTION__ << std::endl;
}
Tracer(const std::string & name)
:m_name(name)
{
std::cout << "[" << m_name << "] " << __PRETTY_FUNCTION__ << std::endl;
}
Tracer(const Tracer & other)
:m_name(other.m_name)
{
std::cout << "[" << m_name << "] " << __PRETTY_FUNCTION__ << std::endl;
}
Tracer(const Tracer && other)
:m_name(other.m_name)
{
std::cout << "[" << m_name << "] " << __PRETTY_FUNCTION__ << std::endl;
}
Tracer & operator=(const Tracer & other) {
m_name = other.m_name;
std::cout << "[" << m_name << "] " << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Tracer & operator=(const Tracer && other) {
m_name = other.m_name;
std::cout << "[" << m_name << "] " << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
~Tracer() {
std::cout << "[" << m_name << "] " << __PRETTY_FUNCTION__ << std::endl;
m_name="You looser!";
}
std::string m_name;
};
//Тот щит, ради чего всё затевалось
template<class T> const T& Min(const T &x, const T &y) { return (x.m_name < y.m_name) ? x : y; }
int main() {
const Tracer& mr = Min(Tracer("a"), Tracer("b"));
cout<<"Some work with mr: "<<mr.m_name<<endl;
return 0;
}
http://ideone.com/4J873s
А ошибка, имхо, именно в сохранении ссылки на временный объект (один из трейсеров) в строке 53... Интересно, почему в этом коде временный объект умирает после вывода, а не до?
Избавится от const Tracer& не всегда возможно. Может быть важно для оптимизации. Юзают в паттерне RAII типа scope_exit. Иногда ссылка нужна для полиморфизма. Иногда нужна для того чтобы не было копирования. Да просто ответственность по возвращаемым ссылкам нужно возлагать на писателя функций, чтобы не плодить правила (правило: не возвращай ссылки на временные объекты - лучше из этого правила просто убрать последние слова и переписать как не возвращай ссылки вообще). В конце концов когда в функции вдруг появятся редкие условия с временными объектами, то ты вдруг как оказалось нарушил и ещё одно правило с возвращаемыми ссылками. Я тоже вспомнил, что я лажал именно так же. Банально соптимизировал. А потом через полгода исправил ошибку добавив редко исполняемое условия с временным объектом. Сейчас я понял что тогда исправив ошибку - я добавил новую. И эта ошибка не исправлена до сих пор в коде где-то.
А где можно об этом почитать?
Ну а про невозврат ссылок согласен. Правда иногда надо, например скобки у контейнеров.
& - не продливает.
const & продливает на один уровень. Ну то есть если ты напишешь перекачивание ссылки: из const A & в const A &, а из неё в const A& или в const B&, то это фейл. В конце жизни второй ссылки все пойдет в помойку и в третей ссылке уже может оказаться говно. Хотя конечно не обязано и компиляторы в некоторых случаях по ошибке или по стандарту могут сохранять жизнь и поэтому код может показывать неплохие жизненные показатели от компилятора к компилятору или от ключей компиляции к ключам компиляции.
Также ссылка до конца выражения доживет, например выражение:
f(m(), g(c, t(), d),m()); функция или конструктор g второй параметр принимает по константной ссылке, то до конца вызова g t() будет жить. После начала кода вызова f - t() уже если я правильно помню помрет. То есть сформированный во время вызова g временный объект (будь то объект типа g в случае конструктора или объект другого типа, если g функция) уже не будет иметь доступа к живому результату работы t(). Тот уже откинет копыта. Все что выше говорил - относится лишь к константным ссылкам. Не константные не продлевают, а это значит f(g()) - или UB или ошибка компиляции, если f принимает не константную ссылку. Вроде это любили превращать в UB без ошибки компиляции студии старые.
все заповеди там
[class.temporary]/5
(std::initializer_list и конструкторам вызванным посредством {...} и агрегатным типам). Происходит ли при этом копирование объектов из {...}. Где они располагаются? В стеке? Как скоро умирают по отношению к ссылкам на них? Как это совместимо с alloca? Если кинет исключение 3тий объект в списке инициализации, то что произойдет? Все отматается? В обратном порядке?
А что за паттерн? С ведрофона гуглить лениво.
Потому что функция возвращает ссылку ;) По-моему этого факта достаточно, чтобы, по крайней мере, внимательно относиться к такой функции.
(протестил на 4.8.2 - с -Wall -Wextra нет ворнинга.)
как по мне: вот к чему приводит злоупотребление reference'ами. ;-)
PS ща пофикшу. заменяешь вот это:
template<class T> const T& Min(const T &x, const T &y) { return (x.m_name < y.m_name) ? x : y; }
на вот это:
#define Min( x, y ) ( (x).m_name < (y).m_name ? x : y )
и, о чудо, оно работает!
PPS я в таких случаях еще вывод (void *)this добавляю что бы видно было как именно объекты глючат.
тестил, создает еще одну инстанцию объекта автоматом. и печатает корректно "а".
Я правильно понял?