Oberon space

General Category => Общий раздел => Тема начата: Berserker от Март 14, 2013, 04:55:01 pm

Название: C++ инициализация ссылок
Отправлено: Berserker от Март 14, 2013, 04:55:01 pm
Подскажите, пожалуйста, если есть знатоки.
Обыскал весь инет, везде твердится одно и то же: ссылки инициализируются единожды. Повторная установка невозможна.

Как тогда GNU C++ компилирует это:

for (size_t i = 0; i < scheta_.size(); i++)
{
  const Schyot &schyot = scheta_; // На каждой итерации разный объект
  ..
}
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 14, 2013, 05:18:30 pm
Подскажите, пожалуйста, если есть знатоки.
Обыскал весь инет, везде твердится одно и то же: ссылки инициализируются единожды. Повторная установка невозможна.

Как тогда GNU C++ компилирует это:

for (size_t i = 0; i < scheta_.size(); i++)
{
  const Schyot &schyot = scheta_; // На каждой итерации разный объект
  ..
}
А какие проблемы? Время жизни переменной - блок. При каждой итерации "блок" создается заново. То есть все переменные что объявлены в блоке создаются заново (понятно что там могут быть оптимизации, но семантически это так).

То же самое что и в случае функции:
void foo(int i) {
    const Bar b& = some_bar[i];
    std::cout << b << std::endl;
}

void boo() {
    for (size_t i=0; i<100500; i++) foo(i);
}

Разницы никакой (более того, и код будет сгенерирован наверняка идентичный).

PS. А что такое GNU C++? Вроде бы обычный стандартный C++98 в данном коде.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 14, 2013, 05:22:48 pm
На всякий случай поясню - объявление переменной по месту использования в С++ это НЕ синтаксический сахар над VAR-блоком Оберона, тут реально другая семантика, и в том же Обероне подобно сэмулировать вменяемым образом невозможно. Соответственно нельзя нормально понять семантику С++ не выйдя за границы семантики Оберона.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 14, 2013, 05:40:44 pm
Спасибо. Именно то, что я искал. Значит время жизни переменной в теле цикла — одна итерация. Сбил меня ответ по C#:

Цитировать
Most of the time, it does not matter whether you declare a variable inside or outside the loop; the rules of definite assignment ensure that it doesn't matter.

По поводу отличий от Оберона, так я уже и так освоился. В том числе с существенной частью особенностей последнего стандарта.

GNU C++ Compiler имелся в виду.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 14, 2013, 05:55:27 pm
Спасибо. Именно то, что я искал. Значит время жизни переменной в теле цикла — одна итерация. Сбил меня ответ по C#:

Цитировать
Most of the time, it does not matter whether you declare a variable inside or outside the loop; the rules of definite assignment ensure that it doesn't matter.
А при чем тут шарп? Это вообще другой язык, к плюсам не имеющий никакого отношения. Там ссылок аналогичных плюсам просто нет.

С тем же успехом можно было читать ответ по Аде или там хаскелю :-)
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 01:09:09 am
Кстати, а для чего и зачем C++ используешь?
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 15, 2013, 04:19:39 pm
Ссылки — это автоматически разыменовываемые указатели. Практически полный аналог (*obj). И реализуются они в 99.99% (если не 100%) через указатели.
Почему ссылки запретили явно переустанавливать не ясно, в итоге функциональность пострадала, а плюсов не видно.

При желании можно изменить ссылку вполне легально.

Код: (c++) [Выделить]
string s[3];
s[0] = "1";
s[1] = "2";
s[2] = "3";

int i = 0;

reseat_reference: const string &ref = s[i];
kon << ref;
i++;

if (i <= 2)
{
  goto reseat_reference;
}

Результат: 123 в консоли.

Цитировать
Кстати, а для чего и зачем C++ используешь?
Первый раз в образовательных целях. Простейший сервер, выдающий по прямому запросу текущее изображение рабочего стола в jpg-формате. Второй раз были лабораторные студентам.

Сейчас начал переписывать модуль с Делфи на С++, виртуализирующий набор WinAPI для работы с файловой системой. В результате можно подключать любые папки в качестве корневого каталога программы для реализации системы модов. Ещё не дописал.

Сейчас нужно сделать эмулятор работы терминала банка и самого банка для студентов в рамках изучения дисциплины «Проектирование информационных систем» и генерации кода из UML-диаграм в Rational Rose 2003.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 04:37:59 pm
Ссылки — это автоматически разыменовываемые указатели. Практически полный аналог (*obj). И реализуются они в 99.99% (если не 100%) через указатели.
Почему ссылки запретили явно переустанавливать не ясно, в итоге функциональность пострадала, а плюсов не видно.

При желании можно изменить ссылку вполне легально.

Код: (c++) [Выделить]
string s[3];
s[0] = "1";
s[1] = "2";
s[2] = "3";

int i = 0;

reseat_reference: const string &ref = s[i];
kon << ref;
i++;

if (i <= 2)
{
  goto reseat_reference;
}

Результат: 123 в консоли.
1) это не изменение ссылки.
2) ссылка это алиас а не аналог указателя.

Перепиши, пожалуйста, вот этот код на указателях:
struct O {int foo;};

O foo() {
    O o = {42};
    return o;
}

int main() {
    const O& o = foo();
    cout << o.foo;
    return 0;
}
Результат конечно должен быть чистым, то есть чтобы при любых флагах компилятора не было варнингов.

Цитировать
Сейчас начал переписывать модуль с Делфи на С++, виртуализирующий набор WinAPI для работы с файловой системой. В результате можно подключать любые папки в качестве корневого каталога программы для реализации системы модов. Ещё не дописал.

Сейчас нужно сделать эмулятор работы терминала банка и самого банка для студентов в рамках изучения дисциплины «Проектирование информационных систем» и генерации кода из UML-диаграм в Rational Rose 2003.
Прикольно.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 15, 2013, 04:50:47 pm
Цитировать
1) это не изменение ссылки.
А что тогда? Ссылка (на физической уровне указатель) сперва содержит адрес s[0], потом s[1], потом s[2].

Цитировать
struct O {int foo;};

O foo() {
    O o = {42};
    return o;
}

int main() {
    const O& o = foo();
    cout << o.foo;
    return 0;
}

Я не профессионал, поправьте, если ошибусь.

Код: (c++) [Выделить]
const O o = foo();
cout << o.foo;
return 0;

Фактически в Вашем коде:

Код: (c++) [Выделить]
O temp;
foo(&temp);
const O& o = temp;

У меня:

Код: (c++) [Выделить]
O temp;
foo(&temp);
// Здесь могло бы быть бессмысленное копирование временной структуры в такую же временную
const O& o = temp;
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 15, 2013, 05:40:27 pm
Ссылки — это автоматически разыменовываемые указатели. Практически полный аналог (*obj). И реализуются они в 99.99% (если не 100%) через указатели.
Почему ссылки запретили явно переустанавливать не ясно, в итоге функциональность пострадала, а плюсов не видно.

1. Запретили переустанавливать потому что ссылка должна себя вести как объект. В частности, "o1 = o2" должно копировать объект. Т.е., для переустановки ссылки должен быть отдельный синтаксис. Кроме того, подобная "жесткость" позволяет проще читать код - примерно как запрет break в циклах в некоторых языках ;) Если нужно что-то менять  по ходу - всегда есть указатели.
2. Плюсы:
- отсутствие звездочек и стрелочек (самый очевидный)
- перегрузка операторов (ради нее все и затевалось, насколько я помню). Т.е., "p1 + p2" перегрузить нельзя.
- Можно использовать соглашение, по которому ссылка - это ненулевой указатель. Очень удобно, потому что убирает кучу проверок указателя на ноль (код чище) - ссылка никогда не может быть нулевой. И без жутких шаблонов. Единственный недостаток - в отличие от нормального ненулевого указателя ссылку нельзя переинитить, да (но на этот случай можно обратиться к жутким шаблонам).
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 15, 2013, 06:04:36 pm
Но согласитесь, Влад, не так уж сложно было ввести нечто вроде:

set TObj &ref = another_obj;

Цена-то — одно ключевое слово и минимальные изменения в компиляторе.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 15, 2013, 06:19:29 pm
Но согласитесь, Влад, не так уж сложно было ввести нечто вроде:

set TObj &ref = another_obj;

Цена-то — одно ключевое слово и минимальные изменения в компиляторе.
Проблема ИМХО в другом.. Алексей богомерзким примером засандаливает ссылку на локальную переменную определенную в  функции foo() - лично я до такого не опускался... и не уверен,  что это гуд... и даже не знал, что такое возможно... :), век живи век учись... однако.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 06:25:10 pm
Но согласитесь, Влад, не так уж сложно было ввести нечто вроде:

set TObj &ref = another_obj;

Цена-то — одно ключевое слово и минимальные изменения в компиляторе.
Проблема ИМХО в другом.. Алексей богомерзким примером засандаливает ссылку на локальную переменную определенную в  функции foo() - лично я до такого не опускался... и не уверен,  что это гуд... и даже не знал, что такое возможно... :), век живи век учись... однако.
Во-первых это не локальная переменная, а возвращаемое значение (то что было в моем примере).
Во-вторых это да, полностью безопасно и полностью легально в языке (этот кейс явно прописан в стандарте).
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 06:30:55 pm
Но согласитесь, Влад, не так уж сложно было ввести нечто вроде:

set TObj &ref = another_obj;

Цена-то — одно ключевое слово и минимальные изменения в компиляторе.
Цена будет огромна.

Весь смысл ссылок в том, что их нет.

То есть ссылка не существует как отдельная сущность, у ссылки нет адреса. А у указателя адрес есть (и по этому адресу лежит адрес того на что он указывает). Поэтому невозможно поставить эксперимент который показал бы отличие создания новой ссылки (ака алиаса) от модификации имеющейся.

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

SET ИмяВарПараметра = another_object;

Ведь в компиляторе всего пару строк изменить :-)
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 15, 2013, 06:32:11 pm

Во-первых это не локальная переменная, а возвращаемое значение (то что было в моем примере).
Во-вторых это да, полностью безопасно и полностью легально в языке (этот кейс явно прописан в стандарте).
Не совсем так... значение это есть ЗНАЧЕНИЕ.. ,адрес ПЕРЕМЕННОЙ есть адрес...  я говорю про то, что вы не  можете же
сделать  int  & b=456; ... а тут аналогично присваивается ЧТО? впрочем если этот случай рассматривается отдельно.. почему бы и нет..., только стройности языку это не прибавляет...
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 06:44:31 pm

Во-первых это не локальная переменная, а возвращаемое значение (то что было в моем примере).
Во-вторых это да, полностью безопасно и полностью легально в языке (этот кейс явно прописан в стандарте).
Не совсем так... значение это есть ЗНАЧЕНИЕ.. ,адрес ПЕРЕМЕННОЙ есть адрес...  я говорю про то, что вы не  можете же
сделать  int  & b=456; ... а тут аналогично присваивается ЧТО? впрочем если этот случай рассматривается отдельно.. почему бы и нет..., только стройности языку это не прибавляет...

int &b = 456 я сделать конечно не могу, а вот
const int& b = 456 сделать я безусловно могу, это абсолютно валидная операция.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 15, 2013, 06:49:59 pm

const int& b = 456 сделать я безусловно могу, это абсолютно валидная операция.
и... что она делает?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 06:52:57 pm

const int& b = 456 сделать я безусловно могу, это абсолютно валидная операция.
и... что она делает?
Как обычно, создает алиас. Ссылки больше ничего и не делают в общем то, это тупо алиасы.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 15, 2013, 07:00:56 pm

const int& b = 456 сделать я безусловно могу, это абсолютно валидная операция.
и... что она делает?
Как обычно, создает алиас. Ссылки больше ничего и не делают в общем то, это тупо алиасы.
на что?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 07:06:37 pm

const int& b = 456 сделать я безусловно могу, это абсолютно валидная операция.
и... что она делает?
Как обычно, создает алиас. Ссылки больше ничего и не делают в общем то, это тупо алиасы.
на что?
На 456, очевидно. Какими именно механизмами компилятор будет обеспечивать аиасность этого алиаса - дело десятое. В зависимости от ситуации он может использовать то одно, то другое, то комбинацию.

Так что нет, ссылка в плюсах это никак не указатель. У ссылки совершенно другая семантика.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 15, 2013, 07:12:36 pm

const int& b = 456 сделать я безусловно могу, это абсолютно валидная операция.
и... что она делает?
Как обычно, создает алиас. Ссылки больше ничего и не делают в общем то, это тупо алиасы.
на что?
На 456, очевидно. Какими именно механизмами компилятор будет обеспечивать аиасность этого алиаса - дело десятое. В зависимости от ситуации он может использовать то одно, то другое, то комбинацию.

Так что нет, ссылка в плюсах это никак не указатель. У ссылки совершенно другая семантика.
а причем здесь семантика...  ну хорошо , а в чем отличие от  const int b=456 - разве здесь  b не является "алиясом" числа 456?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 07:33:01 pm
а причем здесь семантика...  ну хорошо , а в чем отличие от  const int b=456 - разве здесь  b не является "алиясом" числа 456?
А там начинаются тонкие различия. Ну, например const_cast похоже работает над ними по разному.

void foo(int* p) {*p=12;}

int main() {
    const int& a = 42;
    const int  b = 42;
    foo(const_cast<int*>(&a));
    foo(const_cast<int*>(&b));
    cout << a << " " << b  << endl;
    return 0;
}

На gcc имеем два разных числа. На clang имеем два одинаковых числа :-D
Одно из двух - либо тут мы имеем UB, либо gcc глючит.

$ g++ --version
g++ (Debian 4.4.5-8) 4.4.5
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 15, 2013, 07:37:16 pm
Ду, ну и const int b можно влепить volatile и под него будет гарантированно выделена память (и вполне вероятно, оно и так будет выделено), а вот со ссылкой такой фокус не пройдет.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 15, 2013, 07:43:28 pm
alias - это синоним.

На практике этот синоним поддерживает операцию получения адреса объекта, а значит хранит где-то этот адрес, за исключением оптимизаций. Так вот ячейка памяти, хранящая чей-то адрес, как была указателем, так и остаётся.

Что касаемо оптимизаций, то они просты. Если ссылка на деле указывает на объект в стеке/глобальный объект или как в примере с 456 ни на что не указывает, то нет нужды хранить указатель в памяти и выполнять операцию разыменования. Однако я уже привёл пример с goto, когда реальное значение ссылки меняется. Следовательно, компилятор либо всегда безопасно реализует ссылки через указатели, либо анализирует поток управления.

Ещё раз повторю. Ссылка не синоним. Ведь синоним можно было бы заменить оригинальным объектом во всех местах упоминания. Скажем, если ref является синонимом s[0], то упоминание ref можно было бы заменить на s[0], но это не так по двум причинам:

1) s[0] может скрыто вести к вызову метода, а значит это выражение динамическое. Единственный остающийся вариант всегда указывать на то, что когда-то было s[0] — это хранить указатель или адрес памяти.

2) Пример с goto показывает, что поменять адрес у ссылки можно. После этого она и логически (s[1], s[2]) и физически (&s[1], &s[2]) указывает на новый объект.

Тут у меня вопросов к Defective C++ FQA Lite нет.

P.S. Я ничего не критикую, просто мистика и чёрная магия в описаниях к реальной практике отношения имеют мало.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 15, 2013, 07:45:07 pm
alias - это синоним.


нет  alias - это ПСЕВДОНИМ (т.е. ДРУГОЕ НАЗВАНИЕ ОДНОГО И ТОГО ЖЕ ОБЬЕКТА -так переводится)
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 15, 2013, 07:50:56 pm
Да, я оговорился.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 15, 2013, 08:07:44 pm
Если ссылка на деле указывает на объект в стеке/глобальный объект или как в примере с 456 ни на что не указывает, то нет нужды хранить указатель в памяти и выполнять операцию разыменования.
  пример,  с 456  можно трактовать  - как сохранение адреса области памяти  в которой хранится значение 456 - и если оптимизация отключена - то эта константа ДОЛЖНА быть сохранена  в  какой то  ячейке памяти.. кстати, этот фокус (как напомнил Алексей ),  можно проделать только используя модификатор  const, что в принципе говорит об "особом" варианте интерпретации...
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 16, 2013, 12:56:47 am
Насколько я помню, модификация константного объекта - это UB. Т.е. const_cast валиден только для объекта, который не константен в оригинале (а, например, передан по константной ссылке).
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 16, 2013, 02:04:46 am
Насколько я помню, модификация константного объекта - это UB.

Но вот такой код обязан работать везде и с любыми оптимизациями:
void f(int const& n){const_cast<int&>(n) = 456;}
int n = 123;
f(n);
assert(n == 456);
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 18, 2013, 09:50:18 am

void f(int const& n){const_cast<int&>(n) = 456;}
int n = 123;
f(n);
assert(n == 456);
это понятно и потому и не интересно.. лучше ответьте на следующий вопрос -в чем прелесть
const int t& b = rvalue; в сравнении с const int t b = rvalue;? (именно для этого специального случая  - когда rvalue ЗНАЧЕНИЕ соответствующего типа ;)  этот вопрос в том числе и  к Алексею , коль скоро  он такую хренотень привел... то есть перефразирую вопрос.. А на хрена нам нужно эмулировать первое выражение с помощью указателей?  )
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 18, 2013, 09:52:47 am
сорри, одинокая t - в выражениях случайный артефакт...
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 18, 2013, 10:11:39 am
А на хрена нам нужно эмулировать первое выражение с помощью указателей?  )
Лично я вижу пока  2 пункта:
1. Вы... нуться
2. Отгрести потенциальный геморрой в  будущем..
 :D
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 18, 2013, 04:16:51 pm
в чем прелесть
const int t& b = rvalue; в сравнении с const int t b = rvalue;? (именно для этого специального случая  - когда rvalue ЗНАЧЕНИЕ соответствующего типа ;)

В том, что нет копирования в сучае, если rvalue - тоже ссылка (конкретно для интов, конечно, не акутально)?
X const& f();
X const& x1 = f(); // нет копирования
X const x2 = f(); // есть копирование
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 18, 2013, 06:08:19 pm
в чем прелесть
const int t& b = rvalue; в сравнении с const int t b = rvalue;? (именно для этого специального случая  - когда rvalue ЗНАЧЕНИЕ соответствующего типа ;)

В том, что нет копирования в сучае, если rvalue - тоже ссылка (конкретно для интов, конечно, не акутально)?
X const& f();
X const& x1 = f(); // нет копирования
X const x2 = f(); // есть копирование

  ;D  Влад - не юлите..  я спрашивал вас ровно про тот случай  который просил эмулировать Алексей - а не ту очевидную хрень , на которую вы пытаетесь заострить внимание..
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 18, 2013, 07:57:01 pm
в чем прелесть
const int t& b = rvalue; в сравнении с const int t b = rvalue;? (именно для этого специального случая  - когда rvalue ЗНАЧЕНИЕ соответствующего типа ;)

В том, что нет копирования в сучае, если rvalue - тоже ссылка (конкретно для интов, конечно, не акутально)?
X const& f();
X const& x1 = f(); // нет копирования
X const x2 = f(); // есть копирование

  ;D  Влад - не юлите..  я спрашивал вас ровно про тот случай  который просил эмулировать Алексей - а не ту очевидную хрень , на которую вы пытаетесь заострить внимание..

Чтобы было понятней Владу и все остальным, вот модифицированный текст того примера. Нужно показать пальцем, то есть поставить эксперимент который выявит различие семантики для "const O&" и "const O":

#include <iostream>
using namespace std;
struct O {
int foo;
        O(int i) : foo(i) {cout << "O(" << i <<")\n";}
~O(){cout << "~O(" << foo << ")\n";}
};

int i=0;
O foo() {
    O o(i++);
    return o;
}

int main() {
    {const O& o0 = foo();}
    {const O o1  = foo();}
    return 0;
}
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 18, 2013, 08:02:24 pm
Чтобы было понятней Владу и все остальным, вот модифицированный текст того примера. Нужно показать пальцем, то есть поставить эксперимент который выявит различие семантики для "const O&" и "const O":

#include <iostream>
using namespace std;
struct O {
int foo;
        O(int i) : foo(i) {cout << "O(" << i <<")\n";}
~O(){cout << "~O(" << foo << ")\n";}
};

int i=0;
O foo() {
    O o(i++);
    return o;
}

int main() {
    {const O& o0 = foo();}
    {const O o1  = foo();}
    return 0;
}

Да, естественно тип O можно менять как угодно. Ну и в main'e можно что-нибудь дописывать.
Название: Re: C++ инициализация ссылок
Отправлено: Peter Almazov от Март 19, 2013, 04:01:40 am
А буквы O и o были выбраны чтобы понятней было?
"o0" - это, конечно, сильно.
Сильно усугубляет блевотность.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 19, 2013, 04:43:46 am
А буквы O и o были выбраны чтобы понятней было?
"o0" - это, конечно, сильно.
Сильно усугубляет блевотность.
ну да.. не без вы.. ой программерской эстетики  :D , но если не нравится - используйте вместо структуры обычный встроенный тип - например int, соответственно и функция должна возвращать значение этого же типа.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 19, 2013, 07:06:54 am
А буквы O и o были выбраны чтобы понятней было?
"o0" - это, конечно, сильно.
Сильно усугубляет блевотность.
ну да.. не без вы.. ой программерской эстетики  :D , но если не нравится - используйте вместо структуры обычный встроенный тип - например int, соответственно и функция должна возвращать значение этого же типа.
Нет, нельзя.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 19, 2013, 10:12:58 am

Нет, нельзя.
Ну.. ладно оО, так оО
Название: Re: C++ инициализация ссылок
Отправлено: Peter Almazov от Март 19, 2013, 11:49:39 am
Не оО, а o0.
 8)
Название: Re: C++ инициализация ссылок
Отправлено: Geniepro от Март 19, 2013, 12:50:45 pm
а лучше всё же о_О
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 19, 2013, 01:00:56 pm
Чтобы было понятней Владу и все остальным, вот модифицированный текст того примера. Нужно показать пальцем, то есть поставить эксперимент который выявит различие семантики для "const O&" и "const O":

Могу предположить, что в случае "const O" будет UB при попытке последующего const_cast. Но на практике разницы не будет (сомневаюсь, что какой-то компилятор будет класть такую локальную переменную в RO-память). На практике скорее найдется компилятор, который сделает лишнее копирование для случая "const O" (помню борланд чем-то таким похожим страдал).
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 19, 2013, 02:05:52 pm
Чтобы было понятней Владу и все остальным, вот модифицированный текст того примера. Нужно показать пальцем, то есть поставить эксперимент который выявит различие семантики для "const O&" и "const O":

Могу предположить, что в случае "const O" будет UB при попытке последующего const_cast. Но на практике разницы не будет (сомневаюсь, что какой-то компилятор будет класть такую локальную переменную в RO-память). На практике скорее найдется компилятор, который сделает лишнее копирование для случая "const O" (помню борланд чем-то таким похожим страдал).
Я проверял на gcc и clang - нигде лишних копирований нет в данном коде. На самом деле я подозреваю, что может быть разница если функцию foo засунуть в отдельную динамическую либу (aka .so) -- в этом случае у компилятора меньше возможностей суровой оптимизации.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 19, 2013, 02:08:25 pm
Я проверял на gcc и clang - нигде лишних копирований нет в данном коде. На самом деле я подозреваю, что может быть разница если функцию foo засунуть в отдельную динамическую либу (aka .so) -- в этом случае у компилятора меньше возможностей суровой оптимизации.

Впрочем, наверняка и статическую либу можно (.a) попробовать.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 19, 2013, 02:17:36 pm
Проверил с выносом foo в динамическую либу (g++) - все так же.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 19, 2013, 02:22:34 pm
Не оО, а o0.
 8)
так вот в чем фишка... "истина" нуле, а я то думал.. впрочем воистину С++ язык для не очень простых  людей, я бы сказал совсем мне простых... это надо же так
х..ню напишешь, запустишь, просишь эмулировать...  а потом пару дней думаешь... а что бы это значило.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 19, 2013, 02:36:03 pm
Не оО, а o0.
 8)
так вот в чем фишка... "истина" нуле, а я то думал.. впрочем воистину С++ язык для не очень простых  людей, я бы сказал совсем мне простых... это надо же так
х..ню напишешь, запустишь, просишь эмулировать...  а потом пару дней думаешь... а что бы это значило.
Да нет, с тем что там просил эмулировать благополучно разобрались же - ссылка не эквивалентна указателю и через указатель не эмулируется в общем случае.

Осталось разобраться, в одном конкретном случае, эквивалентна ли ссылка на константу самой константе. Хотя, если подумать... Ссылка это алиас, алиас на константу остается той же самой константой. В чем вопрос, спрашивается? :-)
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 19, 2013, 02:38:57 pm
В чем вопрос, спрашивается? :-)
  На фига козе боян? - кажется так.. :D
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 19, 2013, 06:07:50 pm
const O o1  = foo();
nt i;
cin >> i;

1 раз создаётся объект, затем ожидание ввода, затем он удаляется. GCC не генерирует лишнее копирование.

const O &o1  = foo();
nt i;
cin >> i;

Результат идентичен. Что и требовалось доказать.

В чистом примере от Алексея деструктор вызывался из-за того, что элементы в локальных блоках помещены.

P.S. Отключил оптимизацию, но результат не изменился.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 03:29:25 pm
const O o1  = foo();
nt i;
cin >> i;

1 раз создаётся объект, затем ожидание ввода, затем он удаляется. GCC не генерирует лишнее копирование.

const O &o1  = foo();
nt i;
cin >> i;

Результат идентичен. Что и требовалось доказать.

В чистом примере от Алексея деструктор вызывался из-за того, что элементы в локальных блоках помещены.

P.S. Отключил оптимизацию, но результат не изменился.

Эмм... Ты всего лишь показал то, о чем я говорил - в том примере действительно идентичны поведения были и требовалось поставить такой эксперимент, который выявил бы различия. В блоки я переменные засунул для того, чтобы сгруппировать конструктор-деструктор для каждой из переменной (и там тоже было симметрично). То есть чтобы было не: O(), O(), ~O(), ~O(), а O(),~O(),O(),~O()
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 03:49:25 pm
Итак, отличия таки нашлись.
Ну, во-первых:
если у нас foo() внезапно начнет возвращать не переменную типа O, а таки O& (решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:
#include <iostream>
using namespace std;
struct O {
int foo;
        O(int i) : foo(i) {cout << "O(" << i <<")\n";}
O(const O& r) {foo=r.foo; cout << "Copy constructor\n";}
~O(){cout << "~O(" << foo << ")\n";}
O& operator=(const O& r) {cout << "=\n"; return *this;}
};

O o(0);
O& foo() {
    return o;
}

int main() {
    cout << "=== begin ===\n";
    {const O& o0 = foo();}
    cout << "--- mmMmm ---\n";
    {const O  o1 = foo();}
    cout << "===  end  ===\n";
    return 0;
}

Программа напишет теперь такое:
O(0)
=== begin ===
--- mmMmm ---
Copy constructor
~O(0)
===  end  ===
~O(0)
То есть в случае const O o1 = foo(); будет создавать копию переменной (вызывая для этого копирующий конструктор), а const O& = foo(); как было оптимальным так и останется. Никто ничего не заметит.
http://liveworkspace.org/code/1vbybT$1

Это первое семантическое отличие. А вот второе:
struct Foo
{
   Foo(Foo &&)=delete;
   Foo(int,int) {}
};

Foo create() { return {12,42}; }

int main()
{
   const Foo &t1 = create(); // OK
   const Foo t2  = create(); // error
}
Во втором случае (t2) требуется от типа Foo наличие move semantic (семантики перемещения). http://liveworkspace.org/code/3ylY4h$21

Все это отрылось по результатам обсуждения на rsdn: http://rsdn.ru/forum/cpp/5104989.flat.1
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 03:58:42 pm
Итак, отличия таки нашлись.
Ну, во-первых:
если у нас foo() внезапно начнет возвращать не переменную типа O, а таки O& (решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:

:D ГЫ...  ЭТОТ вариант как раз и не интересовал меня...( поскольку он имеет смысл) в отличие от фигни предложенной вами в начале...
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 03:59:18 pm
Итак, отличия таки нашлись.
Ну, во-первых:
если у нас foo() внезапно начнет возвращать не переменную типа O, а таки O& (решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:

:D ГЫ...  ЭТОТ вариант как раз и не интересовал меня...( поскольку он имеет смысл) в отличие от фигни предложенной вами в начале...
Какой именно?
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 04:03:31 pm
Итак, отличия таки нашлись.
Ну, во-первых:
если у нас foo() внезапно начнет возвращать не переменную типа O, а таки O& (решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:

:D ГЫ...  ЭТОТ вариант как раз и не интересовал меня...( поскольку он имеет смысл) в отличие от фигни предложенной вами в начале...
Какой именно?
Самый первый - обычное ЗНАЧЕНИЕ возвращаемое функцией (содержащееся в локальной переменной ).
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 04:06:10 pm
(решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:


Вопрос - в каком месте прописана  заведомая "оптимальность" выбора?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 04:16:32 pm
(решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:


Вопрос - в каком месте прописана  заведомая "оптимальность" выбора?
Оптимальность определяется в контексте конкретного кода, задачи, ресурсов и ТЗ.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 04:20:36 pm
:D ГЫ...  ЭТОТ вариант как раз и не интересовал меня...( поскольку он имеет смысл) в отличие от фигни предложенной вами в начале...
Какой именно?
Самый первый - обычное ЗНАЧЕНИЕ возвращаемое функцией (содержащееся в локальной переменной ).
Это была всего лишь иллюстрация того, что семантика ссылки в С++ отличается от семантики любых указателей. То есть ссылка в С++ не является лишь синтаксическим сахаром над указателем.

Далее возникла задача (точнее мне просто стало интересно) показать отличие const Foo foo от const Foo& foo не в качестве аргументов функций. Что вполне удалось.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 04:22:49 pm

Оптимальность определяется в контексте конкретного кода, задачи, ресурсов и ТЗ.
ммм... не то.... ре формулирую вопрос... с чего где прописана ОБЯЗАННОСТЬ компилятора оптимизировать этот момент?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 04:30:31 pm

Оптимальность определяется в контексте конкретного кода, задачи, ресурсов и ТЗ.
ммм... не то.... ре формулирую вопрос... с чего где прописана ОБЯЗАННОСТЬ компилятора оптимизировать этот момент?
Дык, это не оптимизация. Тут имеем тупо алиас на алиас, алиасы, вообще говоря, не должны создавать новых переменных/объектов (в том числе не должны вызывать какие-либо конструкторы). Ну вот оно и не создает.

В случае const Foo bar; имеем не алиас а самостоятельную константу, то есть этот самый bar не должен измениться когда изменится оригинальное значение. Константность у ссылки и у переменной имеет разный смысл.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 04:39:15 pm

Оптимальность определяется в контексте конкретного кода, задачи, ресурсов и ТЗ.
ммм... не то.... ре формулирую вопрос... с чего где прописана ОБЯЗАННОСТЬ компилятора оптимизировать этот момент?
Дык, это не оптимизация. Тут имеем тупо алиас на алиас, алиасы, вообще говоря, не должны создавать новых переменных/объектов (в том числе не должны вызывать какие-либо конструкторы). Ну вот оно и не создает.

В случае const Foo bar; имеем не алиас а самостоятельную константу, то есть этот самый bar не должен измениться когда изменится оригинальное значение. Константность у ссылки и у переменной имеет разный смысл.
1. этот вопрос относился к приведенному в КОНЦЕ варианту фунюкции Foo (возвращающей ссылку на сторонний обьект) -где и нашлось различие.. где гарантия что оно будет наблюдаться во ВСЕХ реализациях  компилятора С++
2. Хорошо , а как вы интерпретируете  const int & i=45; ?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 04:47:04 pm
Дык, это не оптимизация. Тут имеем тупо алиас на алиас, алиасы, вообще говоря, не должны создавать новых переменных/объектов (в том числе не должны вызывать какие-либо конструкторы). Ну вот оно и не создает.

В случае const Foo bar; имеем не алиас а самостоятельную константу, то есть этот самый bar не должен измениться когда изменится оригинальное значение. Константность у ссылки и у переменной имеет разный смысл.
1. этот вопрос относился к приведенному в КОНЦЕ варианту фунюкции Foo (возвращающей ссылку на сторонний обьект) -где и нашлось различие.. где гарантия что оно будет наблюдаться во ВСЕХ реализациях  компилятора С++
Я ровно на это и отвечал. Создание алиаса (ссылки) обязано НЕ ПРИВОДИТЬ к созданию нового объекта и соответственно вызову конструкторов. Иначе это будет не алиас, а самостоятельный объект.

2. Хорошо , а как вы интерпретируете  const int & i=45; ?
Как алиас на число 45. Как именно это будет выглядеть в машкоде - дело десятое. Важна семантика.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 04:56:45 pm
Дык, это не оптимизация. Тут имеем тупо алиас на алиас, алиасы, вообще говоря, не должны создавать новых переменных/объектов (в том числе не должны вызывать какие-либо конструкторы). Ну вот оно и не создает.

В случае const Foo bar; имеем не алиас а самостоятельную константу, то есть этот самый bar не должен измениться когда изменится оригинальное значение. Константность у ссылки и у переменной имеет разный смысл.
1. этот вопрос относился к приведенному в КОНЦЕ варианту фунюкции Foo (возвращающей ссылку на сторонний обьект) -где и нашлось различие.. где гарантия что оно будет наблюдаться во ВСЕХ реализациях  компилятора С++
Я ровно на это и отвечал. Создание алиаса (ссылки) обязано НЕ ПРИВОДИТЬ к созданию нового объекта и соответственно вызову конструкторов. Иначе это будет не алиас, а самостоятельный объект.

2. Хорошо , а как вы интерпретируете  const int & i=45; ?
Как алиас на число 45. Как именно это будет выглядеть в машкоде - дело десятое. Важна семантика.
1. ХОРОШО - тогда вопрос в чем отличие этого варианта от банальной    O &o (без модификатора const )
2.  а как вы интерпретируете  const int i=45; ?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 05:17:12 pm
Как алиас на число 45. Как именно это будет выглядеть в машкоде - дело десятое. Важна семантика.
1. ХОРОШО - тогда вопрос в чем отличие этого варианта от банальной    O &o (без модификатора const )
O& o -- это алиас разрешающий модификацию, то есть он может быть только на не константу.

2.  а как вы интерпретируете  const int i=45; ?
Как создание константы i равной 45.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 05:22:36 pm
Да, нюанс, вот так можно написать:
volatile const int  i = 45;

А вот так нельзя:
volatile const int&  i = 45;
Ошибка компиляции.

Что в общем то логично - ссылка не создает новую переменную или объект, это тупо алиас. А вот определение константы создает новый объект/переменную.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 06:02:46 pm

А вот так нельзя:
volatile const int&  i = 45;
Ошибка компиляции.

Что в общем то логично - ссылка не создает новую переменную или объект, это тупо алиас. А вот определение константы создает новый объект/переменную.
надо же, а у меня компилирует без проблем и предупреждений  ;)
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 20, 2013, 06:44:57 pm

А вот так нельзя:
volatile const int&  i = 45;
Ошибка компиляции.

Что в общем то логично - ссылка не создает новую переменную или объект, это тупо алиас. А вот определение константы создает новый объект/переменную.
надо же, а у меня компилирует без проблем и предупреждений  ;)
У тебя компилятор явно не реализует стандарт С++. Такими компиляторами славится мелкософт, соответственно предполагаю, что собираешь ты студией (и скорее всего у тебя там не отключены нестандартные расширизмы мелкософтовые и изменения в языке).
Собственно разные вменяемые компиляторы можешь попробовать тут: http://liveworkspace.org/code/31Hll5$2

Ошибка такая:
Compilation finished with errors:
source.cpp:2:25: error: volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'
   volatile const int&  i = 45;
                        ^   ~~
1 error generated.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 20, 2013, 08:29:32 pm
У тебя компилятор явно не реализует стандарт С++. Такими компиляторами славится мелкософт, соответственно предполагаю, что собираешь ты студией (и скорее всего у тебя там не отключены нестандартные расширизмы мелкософтовые и изменения в языке).
Да , точно.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 03:54:55 pm
Цитировать
Итак, отличия таки нашлись.
Ну, во-первых:
если у нас foo() внезапно начнет возвращать не переменную типа O, а таки O& (решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:
Разумеется. Присваивание указателя переменной указательного типа не вызывает лишних копирований.

Цитировать
Это первое семантическое отличие. А вот второе:
Небольшая магия, скрывающая отсутствие необходимости вызывать конструктор переноса, поскольку функции create передаётся адрес памяти, куда и будет помещён объект. Ровно такую же магию можно разрешить и указателям. Если в левой части присваивания указатель,а в правой — значение, то указателю будет присвоен адрес на стеке. Собственно, именно так скрыто и идёт работа со ссылками.

Я подытожу. Здесь и далее синоним — дополнительное имя объекта.

1) Ссылки — это автоматически разыменовываемые указатели. В ряде случаев их использование проще соптимизировать компилятору.
2) Ссылки не обязательно инициализируются один раз. Прыжок назад позволит повторить присваивание.
3) Ссылки не являются физическим синонимом объекта (alias), поскольку их срок жизни может быть больше срока жизни объекта, то есть ссылка может хранить адрес удалённого объекта или объекта на стеке. Ссылки не являются логическим синонимом объекта, так как их можно переустановить.
4) Ссылки могут содержать любые адреса, включая null. Абсолютно легальный код без предупреждений:

Struct* p = NULL;
Struct& ref = *p;

5) Физическая привязка к объекту невозможна без хранения информации о нём. Практически всегда (за вычетом оптимизаций) это адрес объекта. Следовательно, во-первых, ссылка занимает дополнительную память (синоним, как утверждают, памяти дополнительной не забирает), во-вторых, оперирует исключительно адресом, в независимости от его верности: в примере выше разыменование не вызывает исключения, а &ref возвращает 0. В-третьих, поскольку ссылка хранит указатель, то возникают накладные расходы на разыменование, чего не должно быть у синонима.

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

7) Страуструп запретил переустанавливать ссылки по субъективной личной причине неудачного опыта с Algol68:
Цитировать
Цитировать
The reason that C++ does not allow you to rebind references is given in Stroustrup's "Design and Evolution of C++" :

It is not possible to change what a reference refers to after initialization. That is, once a C++ reference is initialized it cannot be made to refer to a different object later; it cannot be re-bound. I had in the past been bitten by Algol68 references where r1=r2 can either assign through r1 to the object referred to or assign a new reference value to r1 (re-binding r1) depending on the type of r2. I wanted to avoid such problems in C++.

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

8) Указатель и ссылкы взаимоконвертируемы:
Цитировать
The C++ standard specifies that a cast reinterpret_cast<U&>(t) is equivalent to *reinterpret_cast<U*>(&t)

9) Личное субъективное наблюдение.  С++ FAQ и адепты языка зачастую повторяют заученные мантры и в этом плане напоминают культистов. Очень странно, учитывая, что большинство из них практикующие профессионалы, понимающие, что стоит за высокопарными фразами, не имеющими к практике никакого отношения.

10) Ничего не имею против автоматически разыменовываемых указателей. Однако возводимая на пустом месте сложность и высокопарность лишь запутывает людей при изучении языка. Все магические свойства ссылок вытекают из того факта, что (*p) не может быть nullptr без вылетов при доступе к объекту. Однако все недостатки и свойства указателей сохраняются: удалённые объекты, нулевые указатели и адреса на стеке могут быть значениями ссылок.

Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 07:02:56 pm
1) Ссылки — это автоматически разыменовываемые указатели. В ряде случаев их использование проще соптимизировать компилятору.

Тут valexey уже кучу доводов привел почему ссылки - не указатели. Все мало? :)

2) Ссылки не обязательно инициализируются один раз. Прыжок назад позволит повторить присваивание.

Прыжок назад - это новая ссылка. Отвыкаем думать в терминах VAR секции на всю процедуру ;) Семантически переменная "создается" в месте объявления и разрушается в конце блока. Что там происходит в случае goto - даже думать не хочу (неинтересно). Вот в таком коде:
for(int i = 0; i < 10; i++)
{
     int var = 1;
     int const& ref = i;
}
переменная var и ссылка ref будет создана и разрушена 10 раз. Никаких "повторных присваиваний" тут нет.

4) Ссылки могут содержать любые адреса, включая null. Абсолютно легальный код без предупреждений:

Struct* p = NULL;
Struct& ref = *p;

Ссылка не может содержать NULL и приведенный код невалидный (UB в момент "*p"). Да, компиляция без предупреждений - частный случай UB.

7) Страуструп запретил переустанавливать ссылки по субъективной личной причине неудачного опыта с Algol68:

Вполне валидная причина, нет?

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

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

10) Ничего не имею против автоматически разыменовываемых указателей. Однако возводимая на пустом месте сложность и высокопарность лишь запутывает людей при изучении языка.

Так что делать с перегружаемыми операторами в отсутствии ссылок и в присутствии адресной арифметики? Например в шарпе (при том что там нет адресной арифметики) операторы перегружается довольно странно (с обязательным паттерном проверки на нулевость, хе-хе).
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 07:21:25 pm
Ну и еще одно отличие ссылки от указателе - указатель это first-class citizen в С++. С ним можно делать ровно то же самое, что и с переменной любого другого типа. Ну, кроме всего прочего, у него например можно взять адрес.

А вот ссылка - нет. Это не first-class citizen. У ссылки нет собственного адреса, ссылку нельзя копировать. Не бывает указателя на ссылку (а вот указатель на указатель бывает!) и не бывает ссылки на ссылку.

Да, и в рассуждениях выше, у меня сложилось впечатление, что Berserker регулярно путает понятие адреса и указатея. Это две принципиально разные сущности :-D

В некоторых случаях реализация ссылки действительно делается через использование адресов, но ссылки никогда не используют указатели. Впрочем, это все уже нюансы какой-то конкретной реализации под какую-то конкретную платформу.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 07:45:29 pm
Цитировать
Тут valexey уже кучу доводов привел почему ссылки - не указатели. Все мало?
Не вижу ни одного примера, который бы противоречил моим словам.

Цитировать
Прыжок назад - это новая ссылка. Отвыкаем думать в терминах VAR секции на всю процедуру
Вообще-то я думаю в терминах реального железа, ассемблера и конструкций С++.

Цитировать
Что там происходит в случае goto - даже думать не хочу (неинтересно).
Там осуществляется переход назад командой jmp/jxx, так же, как и в случае цикла. После чего снова выполняется выражение и конкретная ячейка памяти, которую вы зовёте ссылкой, получает конкретное новое значение. Ну никакой магии и всё абсолютно в терминах и по правилам языка.

Цитировать
переменная var и ссылка ref будет создана и разрушена 10 раз. Никаких "повторных присваиваний" тут нет.
Про блоки мне уже пояснили.

Цитировать
Ссылка не может содержать NULL и приведенный код невалидный (UB в момент "*p"). Да, компиляция без предупреждений - частный случай UB.
Не может в каком мире? В каких компиляторах не может? Все проверки включены, ни одна низкоуровневая возможность не использована. Код абсолютно корректен с точки зрения языка. Не Вы ли писали, что ссылка гарантирована не NULL, не мёртвая и не висячая? Какой смысл писать то, что не соответствует действительности? А теперь попробуйте сделать то же самое в Обероне. Чтобы VAR-параметр вылетал при обращении к нему или содержал мусор. Вот Оберон может компилировать под разные целевые платформы с разными возможностями организации динамической памяти. А С++ ближе к железу всё-таки.

Цитировать
Вполне валидная причина, нет?
Нет.
1) Страуструп всё-таки оставил один и тот же синтаксис и для присваивания значения переменной и для инициализации ссылки.
2) И первый пункт разрулить и убрать неоднозначность можно было бы при помощи введения ключевого слова или нового оператора (например, ===).

Цитировать
Приведи хоть один пример где это нужно и который не может быть переписан без потери наглядности.
Элементарно. Мне нужно обменять адреса двух ссылок. То есть swap со скоростью указателей. В парсере PrevLexem и ThisLexem постоянно обмениваются. Негоже мне вызывать тормознутый std::swap с реальным копированием. Вот и приходится возвращаться к указателям. И да, лексемы нужно уметь устанавливать (на самом деле устанавливать приходится много полей, которые можно было бы сделать ссылками). При этом синтаксис указателей мне абсолютно не нужен. Он неудобен и излишен (тоже один из вопросов критики С++: два оператора для доступа к элементам структур).

Цитировать
Так что делать с перегружаемыми операторами в отсутствии ссылок
А зачем убирать ссылки? Достаточно дать возможность их менять, обменивать и т.д. Хотя скажу честно. Если бы доступ к полям структур был единым через "." и для переменных-указателей и для обычных, то ссылки стали бы менее нужны. Фактически, у них осталось бы только инициализация при объявлении возможно(!) корректным значением.

Цитировать
В некоторых случаях реализация ссылки действительно делается через использование адресов, но ссылки никогда не используют указатели. Впрочем, это все уже нюансы какой-то конкретной реализации под какую-то конкретную платформу.
Какие компиляторы реализуют ссылки не как указатели и пользовались ли Вы такими хоть раз в жизни? Представляете ли Вы себе реализацию компилятора по стандарту С++, где ссылки на физическом уровне не являются указателями? Указатель — это ячейка памяти (2, 4, 8 байтов), хранящая адрес блока памяти.

Цитировать
Ну и еще одно отличие ссылки от указателе - указатель это first-class citizen в С++. С ним можно делать ровно то же самое, что и с переменной любого другого типа. Ну, кроме всего прочего, у него например можно взять адрес.
Товарищи, смотрите:

1) В язык добавляется ссылка как автоматически разыменовываемый указатель. При присваивании адреса (а за именем переменной стоит её адрес, за возвращаемой из функции ссылкой — адрес и т.д) он ведёт себя как указатель. Во во всех остальных случаях как (*p).
2) Добавляется синтаксический сахар с целью оптимизаций. Такой сахар можно добавить и указателям. Он имеет под собой исключительно физическую подоплёку.
3) Накладывается ряд ограничений на использование ссылок в языке. Эти ограничения облегчают оптимизации, да, но не меняют физического/логического смысла ссылок и в свою очередь сужают возможности для их применения.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 08:04:37 pm
Цитировать
В некоторых случаях реализация ссылки действительно делается через использование адресов, но ссылки никогда не используют указатели. Впрочем, это все уже нюансы какой-то конкретной реализации под какую-то конкретную платформу.
Какие компиляторы реализуют ссылки не как указатели и пользовались ли Вы такими хоть раз в жизни? Представляете ли Вы себе реализацию компилятора по стандарту С++, где ссылки на физическом уровне не являются указателями? Указатель — это ячейка памяти (2, 4, 8 байтов), хранящая адрес блока памяти.
Да, конечно пользовался и пользуюсь. Да, все ими пользуются. :-)

Простой пример:
int main() {
    volatile int i;
    volatile int& rep = i;
    volatile int* volatile ptr = &i;
    i    = 1;
    rep  = 2;
    *ptr = 3;
    return 0;
}

Берем clang и генерим код. Смотрим в сгенереный код:
define i32 @main() nounwind {
  %i = alloca i32, align 4                        ; <i32*> [#uses=3]
  %ptr = alloca i32*, align 4                     ; <i32**> [#uses=2]
  volatile store i32* %i, i32** %ptr
  volatile store i32 1, i32* %i
  volatile store i32 2, i32* %i
  %1 = volatile load i32** %ptr                   ; <i32*> [#uses=1]
  volatile store i32 3, i32* %1
  ret i32 0
}
Найди мне тут пожалуйста "указатель" через который реализовалась ссылка ref. Собственно под ref вообще ничего не выделяется ни на стеке, ни где-то там еще. Её в сгенереном коде нет вовсе.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 08:11:42 pm
Потому что это один из банальных моментов, который оптимизируется на ура.
Ведь локальная i - это что-то из серии EBP-4. То есть адреса, которые известны на этапе компиляции, разумеется не используются. А вот если у вас будет возврат ссылки из функции, то гляньте код. Там будет адрес.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 08:20:49 pm
Потому что это один из банальных моментов, который оптимизируется на ура.
Ведь локальная i - это что-то из серии EBP-4. То есть адреса, которые известны на этапе компиляции, разумеется не используются. А вот если у вас будет возврат ссылки из функции, то гляньте код. Там будет адрес.

Ты недооцениваешь современные компиляторы и C++ :-)

int some = 13;
int& two() {return some;}

int main() {
    int& ref = two();
    ref = 42;
    return 0;
}

Сгенерированный код:
@some = global i32 13, align 4                    ; <i32*> [#uses=2]

define i32* @_Z3twov() nounwind readnone {
  ret i32* @some
}

define i32 @main() nounwind {
  store i32 42, i32* @some
  ret i32 0
}

Покажи мне тут, пожалуйста, где у нас ссылка в сгенерированном коде :-)

Вообще, это очень распространенная ошибка - пытаться разобраться в семантике языка пероецируя все в байтики и инструкции процессора (причем в сверически-наивной реализации под какую-то одну конкретную архитектуру). Эдак можно додуматься до того, что в обероне локальные переменные все на стеке всегда живут :-)
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 08:21:41 pm
Потому что это один из банальных моментов, который оптимизируется на ура.
Ведь локальная i - это что-то из серии EBP-4. То есть адреса, которые известны на этапе компиляции, разумеется не используются. А вот если у вас будет возврат ссылки из функции, то гляньте код. Там будет адрес.
Да, и я не знаю что такое EBP-4. Скажем в процессорах с которыми я имею дело, такой штуки в принципе нет. :-)
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 08:26:17 pm
Адрес локальной переменной для текущей процедуры. Не суть. Была бы глобальная переменная по адресу 0x400000, для ссылки точно так же ничего бы не генерировалось, так как вместо (*p) имеем (*((T*) 0x400000)).

Цитировать
Покажи мне тут, пожалуйста, где у нас ссылка в сгенерированном коде :-)
Эм, процедура встроилась (inlined), затем то, что указано выше. Ровно так же можно и с указателем оптимизировать, если агрессивно.

Алексей, Вы как будто не поняли моих слов. Ссылки при известном строгом адресе оптимизируются. Но это простые случаи. Когда же имеем код общего вида, где ссылка устанавливается в конструкторе или функция возвращает созданный объект, то оптимизации уже невозможны и адрес приходится хранить. А по поводу оптимизаций, так даже неиспользуемые переменные можно выкидывать, функции встраивать, циклы раскручивать. Только о чём это говорит кроме факта оптимизации?
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 08:29:59 pm
Вообще-то я думаю в терминах реального железа, ассемблера и конструкций С++.

В терминах реального железа и ассемблера ссылка в большинстве случае будет выглядеть как ячейка с адресом. И этим идентична указателю. С этим никто не спорит. Но как конструкция С++ она довольно сильно отличается от указателя. Этими отличиями можно пользоваться во благо (я там выше писал, почему ссылки полезны). В том числе и тем, что ссылка инициализируется один раз.

Цитировать
Ссылка не может содержать NULL и приведенный код невалидный (UB в момент "*p"). Да, компиляция без предупреждений - частный случай UB.
Не может в каком мире?

В мире корректных C++ программ. Код разыменования нулевого указателя не корректен с точки зрения языка. Потому что это явно прописанный в стандарте UB. Т.е., ты теоретически можешь найти компилятор, который будет проверять в рантайме разыменование нулевого указателя и форматировать жесткий диск в этом случае. И такой компилятор при этом может соответсвовать стандарту С++, а твоя программа с таким разыменованием - точно нет.
Почему такая проверка не делается в существующих популярных реализация тоже понятно - эффективность с одной стороны и куча говнокода с другой.

Не Вы ли писали, что ссылка гарантирована не NULL, не мёртвая и не висячая? Какой смысл писать то, что не соответствует действительности?

Она гарантировано не NULL если в программе нет разыменования нулевых указателей. Т.е., в корректной С++ программе. Про мертвые/висячие я вообще ничего не говорил.

А теперь попробуйте сделать то же самое в Обероне. Чтобы VAR-параметр вылетал при обращении к нему или содержал мусор.

В обероне сематника VAR параметра отличается от семантики ссылки в С++. В частности в обероне ты не может вернуть VAR или положить его как поле структуры.

1) Страуструп всё-таки оставил один и тот же синтаксис и для присваивания значения переменной и для инициализации ссылки.
2) И первый пункт разрулить и убрать неоднозначность можно было бы при помощи введения ключевого слова или нового оператора (например, ===).

Мне кажется, что оно просто того не стоит. Обратимся к примеру.

Элементарно. Мне нужно обменять адреса двух ссылок. То есть swap со скоростью указателей.

Зачем? Я не издеваюсь. Просто нет такой задачи самой по себе.

В парсере PrevLexem и ThisLexem постоянно обмениваются.

Ну так меняй указатели, в чем проблема-то? Указатели для того и нужны, чтоб указывать на разные штуки во времени. Приведи, плз, более развернеутый пример, пока не очень догоняю.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 08:38:13 pm
Адрес локальной переменной для текущей процедуры. Не суть. Была бы глобальная переменная по адресу 0x400000, для ссылки точно так же ничего бы не генерировалось, так как вместо (*p) имеем (*((T*) 0x400000)).
У меня там как раз глобальная переменная ссылка на которую возвращается через функцию которая вызывается из другой функции и "присваивается" локальной ссылке.

Еще раз - ссылка это алиас. Это не first-class citizen. Поэтому ссылка штука во-первых более безопасная чем указатель, во-вторых более эффективная и местами более фичастая (ибо позволяет например дать имя результату какого-либо выражения не заводя новую переменную).

Иногда ссылки да, реализуются через адреса. Но реже чем указатели. Семантика ссылок и указателей РАЗНАЯ и ссылки не являются простым синтаксическим сахаром над семантикой указателей.

Если нужна семантика указателей, и ссылка не годится, то следует (в подавляющем большенстве случаев)... не пользоваться указателями, а воспользоваться умными указателями. Голые указатели - они только для крайних случаев (например для реализации умных указателей, для реализации своих менеджеров памяти и так далее).
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 08:41:57 pm
Еще к вопросу об UB, говнокоде и отсутвии нормальной диагностики разыменования нулевых указателей. Вот метод весьма популярного класса весьма популярной библиотеки весьма промышленной конторы M$:
HWND CWnd::GetSafeHwnd() const { return this == NULL ? NULL : m_hWnd; }

Существование этого кода говорит лишь о том, что код гавно, а автор мудак. А не о том, что this в С++  может быть нулем.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 08:43:15 pm
Еще к вопросу об UB, говнокоде и отсутвии нормальной диагностики разыменования нулевых указателей. Вот метод весьма популярного класса весьма популярной библиотеки весьма промышленной конторы M$:
HWND CWnd::GetSafeHwnd() const { return this == NULL ? NULL : m_hWnd; }

Существование этого кода говорит лишь о том, что код гавно, а автор мудак. А не о том, что this в С++  может быть нулем.
Пахнуло MFC :-)
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 08:47:45 pm
Ну вот еще пример.

#include <stdlib.h>
#include <stdio.h>

int two() {return random()%42;}

int main() {
    const int& ref = two();
    printf("%d\n", ref);
    return 0;
}

Сгенерированный код:
define i32 @_Z3twov() {
  %1 = tail call i32 @random()                    ; <i32> [#uses=1]
  %2 = srem i32 %1, 42                            ; <i32> [#uses=1]
  ret i32 %2
}

declare i32 @random()

define i32 @main() {
  %1 = tail call i32 @random()                    ; <i32> [#uses=1]
  %2 = srem i32 %1, 42                            ; <i32> [#uses=1]
  %3 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %2) ; <i32> [#uses=0]
  ret i32 0
}
Задача - найти адрес которым пользуется ref, ну и саму ref заодно :-)
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 08:56:57 pm
Цитировать
Ну так меняй указатели, в чем проблема-то? Указатели для того и нужны, чтоб указывать на разные штуки во времени. Приведи, плз, более развернеутый пример, пока не очень догоняю.
Цитировать
  class Parser
  {
   protected:
    ...

    Lexem this_lexem_data;
    Lexem prev_lexem_data;
    Lexem *this_lexem;
    Lexem *prev_lexem;
   
   ...
   
    enum kErrorLocation
    {
      kThisLexem,
      kPrevLexem
    };
   
   public:
    Parser ():
      this_lexem(&this_lexem_data),
      prev_lexem(&prev_lexem_data)
    {}
   
    void MarkError (const string &error, kErrorLocation error_location)
    {
      const Lexem &lexem = (error_location == kThisLexem) ? *this_lexem : *prev_lexem;
      log
      (
        error + ". Error in file \"" + config_file_name + "\" on line "
        + ToStr(lexem.line) + " at position " + ToStr(lexem.line_pos),
        "ParseModConfig",
        kMsgError
      );
    }
   
    bool ReadLexem ()
    {
      swap(this_lexem, prev_lexem);
      bool result = vfs::ReadLexem(config, *this_lexem);
     
      if (this_lexem->type == kLexError)
      {
        MarkError(this_lexem->error, kThisLexem);
      }
     
      return result;
    }
...
Можно было бы использовать ссылки. А так приходится довольствоваться синтаксисом "->", "*Lexem" и т.д.

Цитировать
Поэтому ссылка штука во-первых более безопасная чем указатель,
Да нет. В дубовом компиляторе в лоб всегда бы выделялся временный объект. От того, что мёртвый код вычищается, а маленькие функции и циклы и вовсе встраиваются, ничего не меняется. Более того, вы теряете понятие накладных расходов на хранение и разыменование, а в большинстве случаев ссылки всё же не указывают на соседние переменные в кадре стека или глобальные объекты, или указывают, но не так банально.

int some = 13;
int& two() {return some;}

int main() {
    int& ref = two();
    ref = 42;
    return 0;
}

=>

int *ref = &some;
(*ref) = 42

=>

*(&some) = 42

=>

some = 42

Цитировать
Задача - найти адрес которым пользуется ref, ну и саму ref заодно :-)
Функция встроилась. Далее используется ref в качестве аргумента. Больше нигде ref не используется. Поэтому временный результат не сохраняется. А вы вот всё же вызовите вслед функцию из внешней среды, например WinAPI с этим самым ref. И он магическим образом обретёт себе хранилище.

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

Цитировать
Существование этого кода говорит лишь о том, что код гавно, а автор мудак. А не о том, что this в С++  может быть нулем.
Хотел ровно ту же фичу применить для JSon объектов. Вызов метода ведёт к проверке this на null (UB не будет, если класс не содержит виртуальных функций), если null — то это пустое значение, обрабатываемое отдельно. Почему же автор мудак?
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 08:58:05 pm
При том, что никто не мешал (кроме ООП головного мозга) сделать свободную функцию (или static метод) с требуемой семнтикой и абсолютно корректную с точки зрения C++:
HWND GetSafeHwnd(CWnd* wnd) const { return wnd == NULL ? NULL : wnd->m_hWnd; }

P.S. Не зря Страуструп жалел, что не сделал this ссылкой, а не указателем. Глядишь такого гавна в MFC было бы меньше.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 09:09:38 pm
А проверка на &this == nullptr не спасла бы отца русской демократии в таком случае? :)
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 09:17:39 pm
Можно было бы использовать ссылки. А так приходится довольствоваться синтаксисом "->", "*Lexem" и т.д.

Декомпозиция и инкапсуляция рулит:
class Lexems
{
public:
    Lexems():
      this_lexem(&this_lexem_data),
      prev_lexem(&prev_lexem_data)
    {}
   
    Lexem& current(){return *this_lexem;}
    Lexem& prev(){return *prev_lexem;}
    void swap(){std::swap(this_lexem, prev_lexem);}
private:
    Lexem this_lexem_data;
    Lexem prev_lexem_data;
    Lexem *this_lexem;
    Lexem *prev_lexem;
};

Все указатели остались во внутренних потрохах Lexems, наружу торчат только ссылки и человеческие методы (возможно swap надо переименовать во что-то более осмысленное). Не? Или lexems.current()/lexems.prev() вызывают нарекания лишними скобочками? Тогда ничем помочь не могу :)

Цитировать
Существование этого кода говорит лишь о том, что код гавно, а автор мудак. А не о том, что this в С++  может быть нулем.
Хотел ровно ту же фичу применить для JSon объектов.

Обрати внимание на мое дополнение к тому посту (про свободную функцию) плз. Прежде чем плодить говнокод.

Вызов метода ведёт к проверке this на null (UB не будет, если класс не содержит виртуальных функций), если null — то это пустое значение, обрабатываемое отдельно. Почему же автор мудак?

Потому что this не может быть нулем. Вызов метода на нулевом this это ничто иное как все то же разыменование нулевого указателя (p->method()), что является оговоренным UB. Виртуальность здесь постольку поскольку (твоему компилятору все-таки придется слазить по нулевому указателю в таблицу виртуальных функций и обломаться).
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 09:19:15 pm
Функция встроилась. Далее используется ref в качестве аргумента. Больше нигде ref не используется. Поэтому временный результат не сохраняется. А вы вот всё же вызовите вслед функцию из внешней среды, например WinAPI с этим самым ref. И он магическим образом обретёт себе хранилище.
Не понял, а для кого я printf вызывал? Это как раз и есть та самая фукнция из внешней среды. Могу еще раз вызвать - результат не изменится :-) C WinAPI (если бы он существовал на линуксе) было бы то же самое.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 09:23:28 pm
Цитировать
Обрати внимание на мое дополнение к тому посту (про свободную функцию) плз. Прежде чем плодить говнокод.
Так я и пришёл к выводу, что null в качестве реального объекта безопаснее. Тем не менее, для класса без наследников, родителей и виртуальных функций обращения к VMT нет, как и самой VMT.

Цитировать
Не понял, а для кого я printf вызывал? Это как раз и есть та самая фукнция из внешней среды. Могу еще раз вызвать - результат не изменится :-) C WinAPI (если бы он существовал на линуксе) было бы то же самое.
Так добавьте ещё один вызов того же printf, но уже с ref * 2. Второй вызов и последующее использование ref не дадут его соптимизировать.
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 09:24:49 pm
А проверка на &this == nullptr не спасла бы отца русской демократии в таком случае? :)

Есть надежда, что писатель лишний раз бы задумался - как может получиться такой объект у которого адрес (&) нулевой. И на стал бы пистаь такую херню.  Потому как в случае установки "this - это указатель" шансов задуматься чуть меньше (указатели естественно могут быть нулевыми). Хотя гарантий, конечно, никаких нет...
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 09:27:51 pm
Все указатели остались во внутренних потрохах Lexems, наружу торчат только ссылки и человеческие методы (возможно swap надо переименовать во что-то более осмысленное). Не? Или lexems.current()/lexems.prev() вызывают нарекания лишними скобочками? Тогда ничем помочь не могу :)
Откровенно говоря, я вообще не вижу в данном случае тут делать prev/current тут, падон, членами. См. begin, end в современном stl'e например. Если прямо таки хочется инкапсуляции, то эти функции не члены могут быть френдами.

Тогда это все будет выглядеть как prev(lexems) и current(lexems), swap(lexems).
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 09:30:14 pm
Цитировать
Не понял, а для кого я printf вызывал? Это как раз и есть та самая фукнция из внешней среды. Могу еще раз вызвать - результат не изменится :-) C WinAPI (если бы он существовал на линуксе) было бы то же самое.
Так добавьте ещё один вызов того же printf, но уже с ref * 2. Второй вызов и последующее использование ref не дадут его соптимизировать.
Эмм.. С чего бы?

define i32 @_Z3twov() {
  %1 = tail call i32 @random()                    ; <i32> [#uses=1]
  %2 = srem i32 %1, 42                            ; <i32> [#uses=1]
  ret i32 %2
}

declare i32 @random()

define i32 @main() {
  %1 = tail call i32 @random()                    ; <i32> [#uses=1]
  %2 = srem i32 %1, 42                            ; <i32> [#uses=2]
  %3 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %2) ; <i32> [#uses=0]
  %4 = shl i32 %2, 1                              ; <i32> [#uses=1]
  %5 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %4) ; <i32> [#uses=0]
  ret i32 0
}
Ровно то же самое. Никаких указателей.
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 09:32:39 pm
Так я и пришёл к выводу, что null в качестве реального объекта безопаснее.

NULL не может быть объектом по определению. NULL - это нулевой указатель и ничего более. Такой указатель не может указывать на объект (явно огворено в стандарте). Даже в экзотических архитектурах, где объекты могут быть размещены по нулевому (0x0000 в терминах железки) адресу, указатель на такой объект (this) не будет равен NULL (потому что NULL будет представлен там каким-то другим значением).
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 09:35:55 pm
Тогда это все будет выглядеть как prev(lexems) и current(lexems), swap(lexems).

Угу. Так тоже можно. Смысл все тот же - не вываливать потроха на пользователя класса и иметь полный контроль над этими потрохами.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 09:54:44 pm
Алексей, расходы на локальную память растут: %1-%4. В локальной памяти хранятся результаты вызовов функций, а указатели не нужны, потому что указатели и есть адреса ячеек %1-%4.

*(&%1) превращается просто в упоминание %1.

Кстати, а как дела с указателями?
int i;

int main ()
{
  int* p = &i;
  (*p) = 42;
  printf("%d", *p);
  return 0;
}

Хороший компилятор соптимизирует до printf("%d", i), что не повысит статус указателя до магического, а просто в очередной раз подтвердит высокий уровень оптимизации.

Цитировать
NULL не может быть объектом по определению. NULL - это нулевой указатель и ничего более. Такой указатель не может указывать на объект (явно огворено в стандарте). Даже в экзотических архитектурах, где объекты могут быть размещены по нулевому (0x0000 в терминах железки) адресу, указатель на такой объект (this) не будет равен NULL (потому что NULL будет представлен там каким-то другим значением).
Имеется в виду распространённая практика null с малой буквы, то есть реальный объект, играющий роль пустышки.

TObject null_obj;
TObject* null = &null_obj;
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 09:57:19 pm
Я бы всё же поднял время редактирования сообщений до минут 5.

Хороший компилятор соптимизирует до printf("%d", i=42).
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 21, 2013, 10:00:22 pm
TObject null_obj;
TObject* null = &null_obj;

Такое решение возможно, но тут правильнее называть такой объект не "null", а "default". И если под практикой имеется ввиду инициализация всех указателей всегда таким умолчательным значением (вместо NULL) - то я против такой практики (похоже на попытку решения проблемы не самыми эффективными средствами).
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 21, 2013, 10:08:16 pm
null — это тип данных и одновременное значение в JavaScript и, в частности, JSON (http://json.org/) формате (собственно, Вы это и без меня знаете :) ). Поэтому имя default как-то не отвечает смыслу. В С++ ведь заняты только NULL и nullptr (макрос и стандартизированное значение).

Нет, поголовно указатели не инициализируются им.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 21, 2013, 10:19:33 pm
Алексей, расходы на локальную память растут: %1-%4. В локальной памяти хранятся результаты вызовов функций, а указатели не нужны, потому что указатели и есть адреса ячеек %1-%4.
Не растут. В IL LLVM просто нет изменяемых "регистров". Каждое новое сложение/умножение/вычитание всегда порождает очередной %n.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 22, 2013, 04:49:56 am
Мда ,.. господа...вам не кажется что закладываться на гипотетическую (или конкретную) реализацию  яп... трактуя особенности семантики .. все равно , что закладываться на UB (аргументация уровня ... а если бы у бабушки были яйца .., или мой дедушка делает так.., или - пожилые люди обычно делают так)..  как -то несерьезно ..
Название: Re: C++ инициализация ссылок
Отправлено: Geniepro от Март 22, 2013, 08:46:13 am
Ну и FFFFFUUUUUUUUUU этот ваш С++ о_О
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 22, 2013, 12:00:41 pm
Ну и FFFFFUUUUUUUUUU этот ваш С++ о_О
В плане завязки на оптимизации хаскель еще хуже :-)
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 22, 2013, 12:03:03 pm
Мда ,.. господа...вам не кажется что закладываться на гипотетическую (или конкретную) реализацию  яп... трактуя особенности семантики .. все равно , что закладываться на UB (аргументация уровня ... а если бы у бабушки были яйца .., или мой дедушка делает так.., или - пожилые люди обычно делают так)..  как -то несерьезно ..
Дык я лично например не закладываюсь. Я вообще всеми силами противился опускания дискуссии до уровня битиков и байтиков конкретной реализации под конкретную платформу. Ибо в байтиках семантику языка не увидеть, максимум что будет видно - одна из проекций оной семантики на конкретную реализацию.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 23, 2013, 07:41:06 am
Ну и FFFFFUUUUUUUUUU этот ваш С++ о_О
   :D Geniepro ---  вы прям как животное (делаете то что нравится, работаете на том что нравится...)
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 23, 2013, 07:42:53 am

Дык я лично например не закладываюсь. Я вообще всеми силами противился опускания дискуссии до уровня битиков и байтиков конкретной реализации под конкретную платформу. Ибо в байтиках семантику языка не увидеть, максимум что будет видно - одна из проекций оной семантики на конкретную реализацию.
Я заметил... что практически ЛЮБОЙ разговор на ЛЮБОМ форуме о С++ сводится к этому..  с чего бы это?  :D
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 23, 2013, 07:50:22 am
Потому что без более-менее точного представления о физической реализации невозможно писать хороший код с точки зрения эффективности. А в С++ в ряде мест используется магия слов и понятий, затуманивающих явления. Те же ссылки невозможно реализовать без указателей в общем случае. Более того, у них все свойства указателей де-факто сохраняются. При этом FAQ пишет такие вещи как «Reference IS Object», что просто улыбка с лица не сходит.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 25, 2013, 02:55:33 pm
Потому что без более-менее точного представления о физической реализации невозможно писать хороший код с точки зрения эффективности. А в С++ в ряде мест используется магия слов и понятий, затуманивающих явления. Те же ссылки невозможно реализовать без указателей в общем случае. Более того, у них все свойства указателей де-факто сохраняются. При этом FAQ пишет такие вещи как «Reference IS Object», что просто улыбка с лица не сходит.
Если нужна эффективная реализация (с точки зрения максимальной отдачи от железа)- используйте ассемблер... меня раздражает другое какого хрена тыкают в реализацию даже в том случае , когда исходная задача не формулируется в низкоуровневых моделях.. лично я вижу одно обьяснение этому  - тяжелое наследие прошлого...
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 25, 2013, 03:23:07 pm
Потому что без более-менее точного представления о физической реализации невозможно писать хороший код с точки зрения эффективности. А в С++ в ряде мест используется магия слов и понятий, затуманивающих явления. Те же ссылки невозможно реализовать без указателей в общем случае. Более того, у них все свойства указателей де-факто сохраняются. При этом FAQ пишет такие вещи как «Reference IS Object», что просто улыбка с лица не сходит.
Если нужна эффективная реализация (с точки зрения максимальной отдачи от железа)- используйте ассемблер... меня раздражает другое какого хрена тыкают в реализацию даже в том случае , когда исходная задача не формулируется в низкоуровневых моделях.. лично я вижу одно обьяснение этому  - тяжелое наследие прошлого...
+1

PS. А ссылки нельзя в общем случае реализовать без использования "указателей", а точнее адресов, но верно и другое - семантику ссылок нельзя реализовать используя ТОЛЬКО указатели.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 25, 2013, 06:44:40 pm
Цитировать
Если нужна эффективная реализация (с точки зрения максимальной отдачи от железа)- используйте ассемблер... меня раздражает другое какого хрена тыкают в реализацию даже в том случае , когда исходная задача не формулируется в низкоуровневых моделях.. лично я вижу одно обьяснение этому  - тяжелое наследие прошлого...
Значит все игры и ресурсоёмкие приложения, включая браузеры, нужно писать на ассемблере? Вы попишите немного, поотлаживайте. Потом скажите, какой там коэффициент полезного действия и при чём тут ассемблер, если С++ включает в себя максимальное число возможностей, присутствующих исключительно с целью контроля эффективности.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 25, 2013, 07:17:43 pm
Цитировать
Если нужна эффективная реализация (с точки зрения максимальной отдачи от железа)- используйте ассемблер... меня раздражает другое какого хрена тыкают в реализацию даже в том случае , когда исходная задача не формулируется в низкоуровневых моделях.. лично я вижу одно обьяснение этому  - тяжелое наследие прошлого...
Значит все игры и ресурсоёмкие приложения, включая браузеры, нужно писать на ассемблере? Вы попишите немного, поотлаживайте. Потом скажите, какой там коэффициент полезного действия и при чём тут ассемблер, если С++ включает в себя максимальное число возможностей, присутствующих исключительно с целью контроля эффективности.
Дешевая патетика.. - не все "игры и приложения".. а критические по производительности их части. Не вижу большого смысла в этих "максимальных возможностях" - если описание САМОГО ЯП не гарантирует их эффективную реализацию.. так... гребанные танцульки вокруг идолища, да срач на форумах... как пример... та же xds... при гораздо меньших данных для оптимизации на высокоуровневых задачах , обеспечивающая одинаковую производительность с лучшими компиляторами си того времени...
Название: Re: C++ инициализация ссылок
Отправлено: Valery Solovey от Март 26, 2013, 07:04:23 pm
В терминах реального железа и ассемблера ссылка в большинстве случае будет выглядеть как ячейка с адресом.
Даже не с адресом, а со словом данных.
И этим идентична указателю. С этим никто не спорит. Но как конструкция С++ она довольно сильно отличается от указателя. Этими отличиями можно пользоваться во благо (я там выше писал, почему ссылки полезны). В том числе и тем, что ссылка инициализируется один раз.
Думаю, как конструкция она больше всего похожа на директивы. Это сущность периода компиляции, указание компилятору. Если бы я захотел сделать реализацию своего языка, то в нём бы это было именно так (но как в плюсах, естественно, не знаю).

Что же касается беспокойства относительно места хранения данных и способа доступа к ним через ссылку, то я бы объяснял это так. Если объект создаётся в куче, то для обращения к нему нужно знать его адрес в памяти. Хранением адреса занимается указатель. Поэтому, в данном случае без указателя не обойтись. Но плюсовый указатель сам по себе имеет родовые проблемы, своеобразное решение которых было найдено через сколько-то десятков лет после создания языка светлыми C++ головами. Этим решением оказалась эфемерная обёртка вокруг интерфейса доступа к данным. В частности, у указателя есть богатый интерфейс, через который можно обращаться к данным различными способами. А ссылка служит эфемерной оболчкой вокруг этого интерфейса, оставив только способы доступа, которые светлые головы посчитали уместными, и сокрыв остальные.

Таким образом, если функция из внешней библиотеки возвращает данные, сохранённые в динамической памяти, а здесь мы присваиваем результат функции ссылке, то во время компиляции будет предусмотрено выделение памяти на стеке для указателя. Однако, поскольку указатель не объявлен в тексте программы, то доступа к нему нет. Но зато была объявлена ссылка, и на этапе компиляции проверяются способы доступа к ссылке, и если они корректны, то доступ переадесуется безымянному указателю.
Название: Re: C++ инициализация ссылок
Отправлено: Valery Solovey от Март 26, 2013, 07:06:34 pm
Ну и FFFFFUUUUUUUUUU этот ваш С++ о_О
   :D Geniepro ---  вы прям как животное (делаете то что нравится, работаете на том что нравится...)
Насчёт животных не скажу, но ставить правильные цели и добиваться их - признак счастливого человека.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 26, 2013, 07:18:27 pm
Думаю, как конструкция она больше всего похожа на директивы. Это сущность периода компиляции, указание компилятору. Если бы я захотел сделать реализацию своего языка, то в нём бы это было именно так (но как в плюсах, естественно, не знаю).
Собственно любой ЯВУ отличает именно то, что в нем есть то, что затем не отображается в машкод. Ну, например типы (их в машкоде просто нет) и все что с ними связано (например проверки валидности приведения типов на этапе компиляции).
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 10:40:00 am
Ну и FFFFFUUUUUUUUUU этот ваш С++ о_О
   :D Geniepro ---  вы прям как животное (делаете то что нравится, работаете на том что нравится...)
Насчёт животных не скажу, но ставить правильные цели и добиваться их - признак счастливого человека.
С этой фразой согласен... вообще.. не очень понимаю только как она следует из оригинальной фразы (Geniepro ), в особенности  торкает слово "правильные"  :D  ;)
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 10:55:37 am
С этой фразой согласен... вообще.. не очень понимаю только как она следует из оригинальной фразы (Geniepro ), в особенности  торкает слово "правильные"  :D  ;)

Цитировать
— Почти все люди говорят себе, что поступают правильно ... . Но это не поднимает их над заурядностью.
http://www.fanfics.ru/read.php?id=40982&chapter=69
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 11:17:06 am
С этой фразой согласен... вообще.. не очень понимаю только как она следует из оригинальной фразы (Geniepro ), в особенности  торкает слово "правильные"  :D  ;)

Цитировать
— Почти все люди говорят себе, что поступают правильно ... . Но это не поднимает их над заурядностью.
http://www.fanfics.ru/read.php?id=40982&chapter=69
ерунда... цель оценок этого уровня.. не поднятие над заурядностью, но достижение консенсуса ( либо собственным эго, либо мнением социума ).
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 11:33:52 am
Цитировать
Если нужна эффективная реализация (с точки зрения максимальной отдачи от железа)- используйте ассемблер... меня раздражает другое какого хрена тыкают в реализацию даже в том случае , когда исходная задача не формулируется в низкоуровневых моделях.. лично я вижу одно обьяснение этому  - тяжелое наследие прошлого...
Значит все игры и ресурсоёмкие приложения, включая браузеры, нужно писать на ассемблере? Вы попишите немного, поотлаживайте. Потом скажите, какой там коэффициент полезного действия и при чём тут ассемблер, если С++ включает в себя максимальное число возможностей, присутствующих исключительно с целью контроля эффективности.

Замечу, что обсуждаемые тут ссылки не являются по сути таковой возможностью, точнее ссылки они в основном не про эффективность, а про целостность и удобство (не синтаксическое удобство - лично я не вижу никакой разницы между написанием a.foo и a->foo, собственно я несколько лет на плюсах писа без ссылок вообще, и не было проблем ни с производительностью (хотя приложения были именно что про производительность) ни с синтаксисом проблем не было - авторазименовывание нафиг не нужно. А вот с нулевыми указателями как раз проблемы были :-) ).

C++ язык действительно высокого уровня. То есть он уровнем выше той же java. Это выражается в том, что в нем возможно создание собственных абстракций, так сказать, высшего порядка. В частности в C++ можно при желании создать конструкции для полноценного паттерн-матчинга.

И именно в сочетании такой вот высокоуровневости с возможностью перекладывать битики с байтиками, и состоит прелесть С++.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 11:43:07 am

Замечу, что обсуждаемые тут ссылки не являются по сути таковой возможностью, точнее ссылки они в основном не про эффективность, а про целостность и удобство (не синтаксическое удобство ...
В том  то и дело, что не смотря на понимание этого , даже вы спустились до уровня конкретной  реализации и "эффективности".. про форумы си- шников я вообще молчу...(чтобы не писать кипятком)...
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 28, 2013, 12:30:59 pm
Цитировать
а про целостность и удобство
Если уж возвращаться к оригинальному заголовку темы, то меня интересовал лишь один вопрос: почему ссылки нельзя изменять? Выяснилось, что физически компиляторы изменение поддерживают, а запрет возник из-за прихоти создателя языка и не имеет под собой никаких оснований.

После этого поступили возражения по поводу того, что ссылки — это псевдонимы и что их в принципе менять нельзя, что, конечно же, неправда. Именно поэтому я был вынужден спуститься ниже и напомнить, что ссылки всё же указатели на физическом уровне, а возможность установки единожды используется компилятором исключительно для оптимизаций. Пример такой оптимизации локальных ссылок Вы и привели, Алексей.

Я просто прошу взглянуть свежим и трезвым взглядом на проблему. Правило одного присваивания — это сфера функциональных языков, не имеющая смысла в императивных. То есть если X — это ссылка на объект типа файловой системы, то никакой причины запретить динамически подменять реализацию файловой системы нет.

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

vector<int> &a = rand() % 100 >= 50 ? b : c;
Это чистой воды ветвление. Почему же нельзя использовать if/else?

Ну и напоследок. Изменение ссылок не влияет на их свойство быть не NULL (хрупкое, конечно, но свойство).

Кстати, те же итераторы сделаны через указатели. Не сложно вызвать какой-нибудь find на пустой коллекции, разыменовать его и привязать объект к ссылке. Будет чистой воды NULL-ссылка в самом что ни на есть корректном коде.

P.S. Нужно вместо UB использовать PB — предсказуемое поведение в реальных реализациях :)
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 12:39:34 pm

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

Имхо : плохой подход... это все равно что в жавке.. отвечая на вопрос почему строки иммутабельные..  вы лезете в реализацию оных...  - нет, ответ на этот вопрос - определение яп.. - просто по тому, что  так ПОСТАНОВИЛИ , что жабостроки иммутабельные... - это заложено в язык (а какими низкоуровневыми механизмами это будет обеспечиваться - пофиг - разумеется, если язык их не определяет )
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 01:06:27 pm
Цитировать
а про целостность и удобство
Если уж возвращаться к оригинальному заголовку темы, то меня интересовал лишь один вопрос: почему ссылки нельзя изменять? Выяснилось, что физически компиляторы изменение поддерживают, а запрет возник из-за прихоти создателя языка и не имеет под собой никаких оснований.
Очевидно, что это не так.

Во-первых: я явно привел пример когда видно, что внутренняя реализация ссылок и указателей может быть различна - в случае gcc у них разные UB в случае const_cast'a.
Во-вторых: аргументы влада.
В-третьих: ссылка гарантирует что переменная валидна. Никаких нулей.

После этого поступили возражения по поводу того, что ссылки — это псевдонимы и что их в принципе менять нельзя, что, конечно же, неправда. Именно поэтому я был вынужден спуститься ниже и напомнить, что ссылки всё же указатели на физическом уровне, а возможность установки единожды используется компилятором исключительно для оптимизаций. Пример такой оптимизации локальных ссылок Вы и привели, Алексей.
Я не очень понимаю различие между реализацией и, гм, оптимизацией реализации :-) В принципе полагаю возможно как минимум часть семантики (если не всю семантику) ссылок реализовать через простое копирование объекта (без вызовов конструкторов, тупо memcpy). И это не будет противоречить стандарту.

Я просто прошу взглянуть свежим и трезвым взглядом на проблему. Правило одного присваивания — это сфера функциональных языков, не имеющая смысла в императивных.
Эмм.. Что за домыслы? Иммутабельность естественно имеет смысл в императивных языках. Функциональность довольно таки ортогональна иммутабельности :-) Впрочем, задам ка я вопрос - какое основное отличие функционального языка от императивного? Вот прям основное-основное.

То есть если X — это ссылка на объект типа файловой системы, то никакой причины запретить динамически подменять реализацию файловой системы нет.
Э? При чем тут реализация?

Я даже скажу более, ссылка — не псевдоним с логической точки зрения.
vector<int> &a = rand() % 100 >= 50 ? b : c;Это чистой воды ветвление. Почему же нельзя использовать if/else?
Конечно это ветвление. И конечно же это не отменяет тот факт, что "a" тут является псевдонимом как и обычно :-) some& foo = expr;  -- тут foo является псевдонимом для результата выражения expr. const int& foo = 42; -- частный случай этого. Тут просто выражение тривиально. Кто сказал что псевдонимы могут быть только детерминированными? :-)

Тернарный оператор - это выражение, так что не вижу никаких противоречий. А вот if..else - это зачем-то statement. Поэтому увы-с, не взлетит.

Ну и напоследок. Изменение ссылок не влияет на их свойство быть не NULL (хрупкое, конечно, но свойство).
Покажи пожалуйста, ну, например на примере java, как ты собираешься бороться с нулями в изменяемых ссылках. То есть чтобы гарантированно, и не зависило от кривости рук программера.

Ну, например на том же примере твоем: vector<int> &a = rand() % 100 >= 50 ? b : c;
Пусть тут будет: 1) if..else. 2) не будет лишних инициализаций.

Кстати, те же итераторы сделаны через указатели. Не сложно вызвать какой-нибудь find на пустой коллекции, разыменовать его и привязать объект к ссылке. Будет чистой воды NULL-ссылка в самом что ни на есть корректном коде.
Нет, итератеры не сделаны через указатели :-) И пусть тебя не смущает разименовывание и стрелочки для доступа к потрохам - это обычные объекты :-) Каждый указатель можно трактовать как итератор, но не каждый этератор является указателем. Вообще, с точки зрения С++, указатели - это такие кривые и недоделанные, кастрированные, итераторы.

А то что ты описал - не будет NULL-ссылкой. Будет UB чистой воды (причем по двум разным причинам одновременно). И нет, это не корректный код. :-)
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 01:28:58 pm
Во-первых: я явно привел пример когда видно, что внутренняя реализация ссылок и указателей может быть различна - в случае gcc у них разные UB в случае const_cast'a.

Гыыы... вот до чего можно дойти изучая С++ (разные UB  - однако - вдумайтесь )- ;D
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 02:01:46 pm
Во-первых: я явно привел пример когда видно, что внутренняя реализация ссылок и указателей может быть различна - в случае gcc у них разные UB в случае const_cast'a.

Гыыы... вот до чего можно дойти изучая С++ (разные UB  - однако - вдумайтесь )- ;D
Ну, а чего удивительного? Если сущность едина, то наверно UB должно как-то одинаково быть. Хотя конечно не фа-акт. :-) Впрочем UB это не единственный вид забавных всяких B. Щща отдельную тему под это дело заведу.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 02:07:50 pm
Если сущность едина, то наверно UB должно как-то одинаково быть.
Едино только понятие UB, а кто кому чего должен - без понятия... :)
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 28, 2013, 03:19:02 pm
Кстати, те же итераторы сделаны через указатели. Не сложно вызвать какой-нибудь find на пустой коллекции, разыменовать его и привязать объект к ссылке. Будет чистой воды NULL-ссылка в самом что ни на есть корректном коде.

Разыменование нулевого указателя - это невалидный код. Уже много раз написали. Что там будет после этого кода (нулевая ссылка) - уже неинтересно. А вообще find на пустой коллекции вернет не NULL, а end() - который скорее всего будет совсем не NULL и разыменование такого итератора скорее всего обрушит дебаг сборку.

Ненулевость ссылок чисто декларативное свойство, а не средство отладки. Представь, что ты написал библиотеку с паблик функцией:
void f(params const* p)
{
    assert(p);
    ...
}

И в документации написал, что в функцию f() передаются параметры, которые не могут быть NULL. Пользователь твоей библиотеки не читает документацию, и передается туда NULL в надежде, что функция может работать с какими-то дефолтовыми параметрами. Все крэшится, нерадивый пользователь лезет читать документацию, передает валидный указатель, компилирует, запускает...

Теперь представь, что ты изначально написал:
void f(params const& p)
{
    // assert теперь не нужен, если тебе пришла нулевая ссылка, то значит в вызывающем коде есть UB, твой код абсолютно корректный
    ...
}
Пользователь твоей библиотеки по-прежнему не читает документацию, но NULL в твою функцию уже не отдаст (если пишет программы без UB). Все в выигрыше - тебе меньше ассертов писать и документации, пользователю меньше читать документации.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 03:55:40 pm
Кстати, те же итераторы сделаны через указатели. Не сложно вызвать какой-нибудь find на пустой коллекции, разыменовать его и привязать объект к ссылке. Будет чистой воды NULL-ссылка в самом что ни на есть корректном коде.

Разыменование нулевого указателя - это невалидный код. Уже много раз написали. Что там будет после этого кода (нулевая ссылка) - уже неинтересно. А вообще find на пустой коллекции вернет не NULL, а end() - который скорее всего будет совсем не NULL и разыменование такого итератора скорее всего обрушит дебаг сборку.
Чуть поясню:

end() вообще нельзя разименовывать (по стандарту), это раз. Его разименовывание это UB.

Два: "end() returns an iterator which is the past-the-end value for the container", даже если у нас итератор в данном конкретном случае вдруг является указателем (что бывает например в случае массивов), то элемент следующий за последним явно будет иметь не нулевой адрес, не nullptr совсем.

Три - чтобы запустить find на "пустой коллекции" нужно получить вначале начальный и конечный
итератор. Если мы итератор начальный получаем через begin(), то begin() на пустом контейнере вернет end() (согласно пункту 7 на стр 541: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf).

Соответственно имеем find(end(vec),end(vec)). Очевидно, что контейнер ака скажем вектор, вовсе не обязан быть пустой :-)
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 28, 2013, 05:38:55 pm
Цитировать
просто по тому, что  так ПОСТАНОВИЛИ
Именно так. Создатель языка взял идею из Алгола и урезал по субъективным причинам.
Исправление могло выглядеть как "let reference = new_object;"

Цитировать
Во-первых: я явно привел пример когда видно, что внутренняя реализация ссылок и указателей может быть различна - в случае gcc у них разные UB в случае const_cast'a.
Во-вторых: аргументы влада.
В-третьих: ссылка гарантирует что переменная валидна. Никаких нулей.
Алексей, смотрите. Использование указателя может транслироваться в:
1) Ничего. Оптимизация при известном адресе значения.
2) Копию константного объекта в виде временной переменной.
3) В полях структур, в сложных выражениях, в результатах невстроенныхфункций — в указатель.

А если проще: без указателей реализовать поддержку ссылок нельзя. Без оптимизаций, сугубо на указателях можно. С этим-то все согласны?

Если разрешить переустанавливать ссылки, то 3-й пункт будет использоваться чуток чаще. При чём анализ потока управления происходит и сейчас. При совершении прыжка назад (goto) ссылка изменяется, а следовательно компилятор ничего не оптимизирует и в лоб выделяет место под неё.

Для гарантии не-NULL нужны проверки времени исполнения, иначе это не гарантии.

Цитировать
Я не очень понимаю различие между реализацией и, гм, оптимизацией реализации :-)
Оптимизация в стандарте не прописана, исходите из предположения её полного отсутствия.

Цитировать
В принципе полагаю возможно как минимум часть семантики (если не всю семантику) ссылок реализовать через простое копирование объекта (без вызовов конструкторов, тупо memcpy). И это не будет противоречить стандарту.
В лоб нельзя. Просто пара причин даже для этого ограниченного случая: goto разрешён (нужно проверять, устанавливается ли ссылка один раз), структурам и стековым кадрам нельзя жиреть (не будете же вы копировать по 5 КБ на объект), объявление указателей/ссылок возможно до полного определения структур.  Поправьте, если где ошибаюсь.

Цитировать
Эмм.. Что за домыслы? Иммутабельность естественно имеет смысл в императивных языках. Функциональность довольно таки ортогональна иммутабельности :-) Впрочем, задам ка я вопрос - какое основное отличие функционального языка от императивного? Вот прям основное-основное.
Исправляюсь. Императивным языкам не свойственна навязанная неизменность. То есть дело не в том, чтобы объявлять саму ссылку const или нет, а в том, что она всегда константная. Возьмите указатели и примените к ним то же решение: указатели всегда константные. Что такое решение даёт программисту?

Цитировать
Э? При чем тут реализация?
MemoryFS mem_fs;
FileFS file_fs;

AnyFS &fs = file_fs;
...
if (some condition) let fs = file_fs; // такой "фичи" нет

Цитировать
Кто сказал что псевдонимы могут быть только детерминированными? :-)
Для меня есть два понятия: указатель на динамическую величину или псевдоним статической. То есть в случае псевдонима мы ещё на этапе компиляции знаем, к чему привязываемся. Но это субъективное восприятие, на дискуссии не настаиваю, да и ценность этого восприятия мала.

Цитировать
Покажи пожалуйста, ну, например на примере java, как ты собираешься бороться с нулями в изменяемых ссылках. То есть чтобы гарантированно, и не зависило от кривости рук программера.

Ну, например на том же примере твоем: vector<int> &a = rand() % 100 >= 50 ? b : c;
Пусть тут будет: 1) if..else. 2) не будет лишних инициализаций.
А мы не отменяем необходимость инициализации при объявлении.

Object &a = b;
...
let a = c;
...
let a = d;

где let — установка указателя, а не изменение значения объекта, на которого ссылаются.

Цитировать
Нет, итератеры не сделаны через указатели :-) И пусть тебя не смущает разименовывание и стрелочки для доступа к потрохам - это обычные объекты :-) Каждый указатель можно трактовать как итератор, но не каждый этератор является указателем. Вообще, с точки зрения С++, указатели - это такие кривые и недоделанные, кастрированные, итераторы.
Да, речь идёт о контейнерах со сплошным хранением элементов. И здесь я имел в виду физическую реализацию, когда итератор является структурой, содержащей один указатель и нет проверок времени исполнения на выходы за границы контейнера.

Не редко вижу в ответах фразы "In fact, vectors iterators are usually implemented as pointers.", но не проверял итераторы в исходниках STL. Хотя более чем уверен, либо просто указатель, либо структура из одного поля с набором требуемых inline-операторов.

Цитировать
А то что ты описал - не будет NULL-ссылкой. Будет UB чистой воды (причем по двум разным причинам одновременно). И нет, это не корректный код. :-)
Неопределённое поведение в стандарте не равно неопределённому поведению в реальности. В моей реальности программисты ошибаются, программы не идеальны, а некорректные ссылки не рушат программы до попытки обращения к объектам.

Цитировать
Ненулевость ссылок чисто декларативное свойство, а не средство отладки. Представь, что ты написал библиотеку с паблик функцией:
Согласен. Но я ни разу не предлагал разрешить ссылкам быть нулевыми. Я предлагал вернуть им возможность указывать на разные существующие объекты.

Цитировать
end() вообще нельзя разименовывать (по стандарту), это раз. Его разименовывание это UB.
Знаю. Но поскольку «нельзя» относится к тем же нельзя, что и «нельзя писать несовершенный код», то в реальности где-то будет опущена проверка результата.

Цитировать
то элемент следующий за последним явно будет иметь не нулевой адрес, не nullptr совсем.
begin() + size() он будет. begin() вернёт nullptr, а size() будет 0.

Цитировать
Три - чтобы запустить find на "пустой коллекции" нужно получить вначале начальный и конечный
итератор.
Ну да, будет возвращён end(), который есть nullptr.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 06:02:53 pm
begin() + size() он будет. begin() вернёт nullptr, а size() будет 0.

Цитировать
Три - чтобы запустить find на "пустой коллекции" нужно получить вначале начальный и конечный
итератор.
Ну да, будет возвращён end(), который есть nullptr.

А если подумать головой? :-)
#include <iostream>
#include <vector>

int main() {
std::vector<int> a;
a.reserve(42);
std::cout << "is empty: " << a.empty() << std::endl;
std::cout << "begin: " << (size_t)&*a.begin()
                  << "\tend: " << (size_t)&*a.end() << std::endl;
return 0;
}

Вывод:
is empty: 1
begin: 4388292592 end: 4388292592

Контейнер пуст, но при этом begin и end не нуль.

PS. Кстати, итераторы не являются указателями, поэтому пришлось писать такое, чтобы получить адрес: (size_t)&*a.begin()

PPS. И кажется я писал, что для твоего примера не нужно пустого контейнера, достаточно в find скормить два end'а произвольного контейнера. find'у вообще пофиг на контейнер.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 28, 2013, 06:12:57 pm
Зачем жульничаете?

is empty: 1
begin: 0        end: 0

std::vector<int> a;
std::cout << "is empty: " << a.empty() << std::endl;
std::cout << "begin: " << (size_t)&*a.begin()
<< "\tend: " << (size_t)&*a.end() << std::endl;

И там именно указатели на выделенную память в ваши 42 элемента. Уберите reserve и увидите то, о чём я писал.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 06:20:36 pm
Зачем жульничаете?

is empty: 1
begin: 0        end: 0

std::vector<int> a;
std::cout << "is empty: " << a.empty() << std::endl;
std::cout << "begin: " << (size_t)&*a.begin()
<< "\tend: " << (size_t)&*a.end() << std::endl;

И там именно указатели на выделенную память в ваши 42 элемента. Уберите reserve и увидите то, о чём я писал.
Там нет ни одного элемента - empty дает true, это железно означает что в векторе нет НИ ОДНОГО элемента. А то что оно там что-то закешировало - это дело десятое. То же самое может получиться после серии вставок и удаления элементов.

Впрочем, оставим вектор в покое. Возьмем map:
#include <iostream>
#include <map>
int main() {
std::map<int,int> m;
std::cout << "is empty: " << m.empty() << std::endl;
std::cout << "begin: " << (size_t)&*m.begin()
                  << "\tend: " << (size_t)&*m.end() << std::endl;
return 0;
}

is empty: 1
begin: 140734916160464 end: 140734916160464

Так что там про нулевость end'а в случае пустого контейнера?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 06:25:48 pm
Список:
#include <iostream>
#include <list>
int main() {
std::list<int> m;
std::cout << "is empty: " << m.empty() << std::endl;
std::cout << "begin: " << (size_t)&*m.begin()
                  << "\tend: " << (size_t)&*m.end() << std::endl;
return 0;
}

is empty: 1
begin: 140734989089752 end: 140734989089752

PS. Ощущаю себя прям таки разрушителем легенд :-)
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 06:37:15 pm

PS. Ощущаю себя прям таки разрушителем легенд :-)
я не улавливаю другое... зачем  Берсеркеру разименовывать итераторы... а х ..да едрить.. соображения "эффективности"...
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 06:42:15 pm

PS. Ощущаю себя прям таки разрушителем легенд :-)
я не улавливаю другое... зачем  Берсеркеру разименовывать итераторы... а х ..да едрить.. соображения "эффективности"...
Не-не-не, разименовывание итератора дает тебе собственно объектик который соответствует данному итератору (но естественно не дает тебе собственно тушку самого итератора, тушка итератора - это итератор и есть). Это абсолютно нормально и правильно.

При этом итераторы почти никогда не бывают сырыми указателями, поэтому итератор соответствующий nullptr - это что-то довольно дикое в общем случае.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 28, 2013, 06:44:09 pm
И снова жульничаете. Я же сказал, для контейнеров со сплошным блоком памяти. Списки определяются как классические двусвязные. Вы видите на консоли адрес на стеке (это много хуже, чем NULL, кстати).

Вот код для вектора из STL:

      // iterators
      /**
       *  Returns a read/write iterator that points to the first
       *  element in the %vector.  Iteration is done in ordinary
       *  element order.
       */
      iterator
      begin() _GLIBCXX_NOEXCEPT
      { return iterator(this->_M_impl._M_start); }

      /**
       *  Returns a read-only (constant) iterator that points to the
       *  first element in the %vector.  Iteration is done in ordinary
       *  element order.
       */
      const_iterator
      begin() const _GLIBCXX_NOEXCEPT
      { return const_iterator(this->_M_impl._M_start); }

      /**
       *  Returns a read/write iterator that points one past the last
       *  element in the %vector.  Iteration is done in ordinary
       *  element order.
       */
      iterator
      end() _GLIBCXX_NOEXCEPT
      { return iterator(this->_M_impl._M_finish); }

Для списка:

      // iterators
      /**
       *  Returns a read/write iterator that points to the first element in the
       *  %list.  Iteration is done in ordinary element order.
       */
      iterator
      begin() _GLIBCXX_NOEXCEPT
      { return iterator(this->_M_impl._M_node._M_next); }

      /**
       *  Returns a read-only (constant) iterator that points to the
       *  first element in the %list.  Iteration is done in ordinary
       *  element order.
       */
      const_iterator
      begin() const _GLIBCXX_NOEXCEPT
      { return const_iterator(this->_M_impl._M_node._M_next); }

struct _List_node_base
    {
      _List_node_base* _M_next;
      _List_node_base* _M_prev;


Цитировать
я не улавливаю другое... зачем  Берсеркеру разименовывать итераторы... а х ..да едрить.. соображения "эффективности"...
Разыменование является нормальным явлением.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 06:46:39 pm

Разыменование является нормальным явлением.
и итераторов в частности?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 06:51:00 pm
И снова жульничаете. Я же сказал, для контейнеров со сплошным блоком памяти. Списки определяются как классические двусвязные. Вы видите на консоли адрес на стеке (это много хуже, чем NULL, кстати).
Не понял, где ты что-то писал про сплошные блоки памяти?
Кстати, те же итераторы сделаны через указатели. Не сложно вызвать какой-нибудь find на пустой коллекции, разыменовать его и привязать объект к ссылке. Будет чистой воды NULL-ссылка в самом что ни на есть корректном коде.
Итого ограничения: пустой контейнер (то есть empty() выдает true), и вызов для него find. Пустой map, list и vector (у которого что-то там унутре зарезервировано) -- вполне подходят.

PS. А адрес чего именно вижу - стека, или кучи, или вообще статической памяти - глубоко пофиг до тех пор, пока реализация контейнера соответствует стандарту. Ничто не мешает написать реализацию vector'а которая будет в некоторых случаях располагать свои элементы на стеке, более того, вектор и вообще стандартная либа может быть ВШИТА в компилятор, и соответственно реализовываться средствами самого компилятора, не имея исходников как таковых.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 06:55:09 pm
лично я вижу здесь одно - Берсеркер пытается  хакнуть stl... вне зависимости от результата, имхо,  не джедайское это дело...
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 06:56:47 pm

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

(разименовывание итератора -- это не разименовывание указателя! это высокоуровневая операция!)
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 07:05:12 pm

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

(разименовывание итератора -- это не разименовывание указателя! это высокоуровневая операция!)
я имею ввиду ручное разименование?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 07:09:38 pm

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

(разименовывание итератора -- это не разименовывание указателя! это высокоуровневая операция!)
я имею ввиду ручное разименование?

Кажется я запутался в твоей путанице терминологий :-)
Давай на примере:
list<int> some_list;
i = find(some_list.begin(), some_list.end(),42);
if (i!=some_list.end()) cout << *i;
Такое - абсолютно корректно. Тут i - это ни разу не указатель.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 28, 2013, 07:23:54 pm
Цитировать
Не понял, где ты что-то писал про сплошные блоки памяти?
Цитировать
Нет, итератеры не сделаны через указатели :-) И пусть тебя не смущает разименовывание и стрелочки для доступа к потрохам - это обычные объекты :-) Каждый указатель можно трактовать как итератор, но не каждый этератор является указателем. Вообще, с точки зрения С++, указатели - это такие кривые и недоделанные, кастрированные, итераторы.
Да, речь идёт о контейнерах со сплошным хранением элементов. И здесь я имел в виду физическую реализацию, когда итератор является структурой, содержащей один указатель и нет проверок времени исполнения на выходы за границы контейнера.

Не редко вижу в ответах фразы "In fact, vectors iterators are usually implemented as pointers.", но не проверял итераторы в исходниках STL. Хотя более чем уверен, либо просто указатель, либо структура из одного поля с набором требуемых inline-операторов.

Цитировать
Итого ограничения: пустой контейнер (то есть empty() выдает true), и вызов для него find. Пустой map, list и vector (у которого что-то там унутре зарезервировано) -- вполне подходят.

Буду краток.

Цитировать
Нет, итератеры не сделаны через указатели :-)
Там, где возможно, сделаны. Например, в векторах. В случае списков возвращается адрес узла, у которого переопределена операция (*). Но вот незадача, содержимое элемента хранится в самом узле, а значит, после того, как end() вернул мусор, мы получили ссылку на мусор. Ссылки на мусор гораздо разрушительнее по последствиям, но это не являлось темой обсуждения. Я просто показал, насколько всё низкоуровнево внутри ради достижения эффективности.

По поводу возможности встраивания STL в сам язык и других предложений — это не ко мне, а к энтузиастам-разработчиком и гипотетическим будущим реализаторам STL.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 07:34:49 pm
Там, где возможно, сделаны. Например, в векторах. В случае списков возвращается адрес узла, у которого переопределена операция (*). Но вот незадача, содержимое элемента хранится в самом узле, а значит, после того, как end() вернул мусор, мы получили ссылку на мусор. Ссылки на мусор гораздо разрушительнее по последствиям, но это не являлось темой обсуждения.
Это зависит от реализации же! Контейнеры вполне могут резирвировать для end'а объект-заглушку. Вот вообще как нефиг делать (более того, подозреваю что в дебажном коде это так и делается, например в мелкософфтоффской реализации stl'я).

Еще раз - что там как унутре хранится в конкретной реализации может быть интересно ну только в очень специфических случаях. Полагаться можно только на то, что прописано в стандарте. Все. Точка.

Изучение языка ковыряясь в байтиках сгенерированных конкретной реализацией не даст понимания языка, в лучем случае даст понимание как иногда работает данная конкретная реализация языка (но при выходе следующей версии этой реализации программы могут просто перестать работать!).

Да.. А потом к нам приходят на собеседование люди с резюме, где в графе "языки программирования" гордо красуется нечто вроде: VisualC++. Но бывает конечно хуже - в графе написано C++, а знает он только Microsoft Visual C++ 2005 под x86 (32 бита). При этом языка не знает, и писать код так чтобы он был без UB (то есть работал всегда и везде, с любой валидной реализацией stl, компилятора и так далее) не умеет. Пичаль...

Я просто показал, насколько всё низкоуровнево внутри ради достижения эффективности.
Весь stl написан крайне высокоуровнево, но при этом он близок к железу. Следует одно от другого таки отличать :-)

По поводу возможности встраивания STL в сам язык и других предложений — это не ко мне, а к энтузиастам-разработчиком и гипотетическим будущим реализаторам STL.
Я к тому, что это явно прописано в стандарте, то есть возможность такого.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 28, 2013, 07:37:01 pm

Такое - абсолютно корректно. Тут i - это ни разу не указатель.
не понял, в общем случае операция разименования перекрывается или нет?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 07:48:58 pm

Такое - абсолютно корректно. Тут i - это ни разу не указатель.
не понял, в общем случае операция разименования перекрывается или нет?
Да. Можно написать свой класс который будет иметь операцию разименовывания.

Выглядит это как-то так:
struct Foo {
    int operator*(){return 42;}
};

Естественно оператор разименовывания не обязан возвращать именно int.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 28, 2013, 07:52:15 pm
Ну вот, Алексей, мы и пришли к заключению. Вы, вероятно, знаете множество реализаций STL для C++. Мой опыт гораздо более скромный. И это скромный опыт говорит, что в STL ради оптимизации везде, где можно, в качестве итераторов используются указатели. Это первое. И второе: нет множества проверок времени исполнения, которые бы гарантировали безопасность контейнеров как абстрактных типов данных. Вместо этого там грабли и грабли.

Я не вижу ничего плохого в том, чтобы знать, где могут быть причины ошибок. Понимание наиболее вероятного варианта реализации позволяет как самому создавать новые реализации, так и ориентироваться в существующих, ну и, разумеется, при отладке. А если на работу не возьмёте, так уж и быть, не обижусь ;)
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 08:01:43 pm
Ну вот, Алексей, мы и пришли к заключению. Вы, вероятно, знаете множество реализаций STL для C++. Мой опыт гораздо более скромный. И это скромный опыт говорит, что в STL ради оптимизации везде, где можно, в качестве итераторов используются указатели. Это первое. И второе: нет множества проверок времени исполнения, которые бы гарантировали безопасность контейнеров как абстрактных типов данных. Вместо этого там грабли и грабли.
Ну, кое-что все же есть. Например at() вместо [] для вектора. И будет вам проверка выхода за границы дозволенного :-)

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

Ну и наконец - никто не мешает нарисовать удобную для тебя обертку над всем этим, которая сама будет проверять на end().

Я не вижу ничего плохого в том, чтобы знать, где могут быть причины ошибок. Понимание наиболее вероятного варианта реализации позволяет как самому создавать новые реализации, так и ориентироваться в существующих, ну и, разумеется, при отладке. А если на работу не возьмёте, так уж и быть, не обижусь ;)
А я на работе на С++ и не пишу практически, так что С++'сник мне никак не нужен :-) Вот от жабоскриптоведа/html-профи я бы не отказался :-) Ну и есть задачи на Go и матлабе, а также на ObjC&C99.

А для того, чтобы знать какие могут быть причины ошибок - нужно таки стандарт (ну или хотя бы более человеческое описание на cppreference.com да www.cplusplus.com/reference/) читать - там написано. Из реализации эти знания выколупывать - занятие не благодарное абсолютно (да еще и ответ может быть не верным).
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 28, 2013, 08:08:30 pm
Читаю всё вместе: критику языка (лучшее в сети — Defective C++ или C++ FQA), cppreference, ответы на stackoverflow ну и отладчик с исходниками STL забывать не стоит. Так сказать, для полноты картины  8)

Однако когда речь идёт о коде, я всегда предпочитаю представлять перед собой то, что стоит за абстракциями. Иногда вплоть до команд. Судя по тому, как пишутся чувствительные к производительности вещи, не только у меня подобное восприятие.
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 28, 2013, 08:23:08 pm
Читаю всё вместе: критику языка (лучшее в сети — Defective C++ или C++ FQA), cppreference, ответы на stackoverflow ну и отладчик с исходниками STL забывать не стоит. Так сказать, для полноты картины  8)

Однако когда речь идёт о коде, я всегда предпочитаю представлять перед собой то, что стоит за абстракциями. Иногда вплоть до команд. Судя по тому, как пишутся чувствительные к производительности вещи, не только у меня подобное восприятие.
Чувствительные к производительности вещи обычно таки имеют довольно узкую нишу в плане железа (иначе нужной производительности не достичь) и там свой подход. Вплоть до выкидывания текущей реализации stl и написания своего велосипеда под особенности задачи, железа, компилятора и операционки.

Ну, например если у меня задача такова, что мне кроме линукса и gcc на нем никто не нужен, да к тому же задача требует множества конкурентных нитей, то я вполне могу заюзать бесконечный растущий системный стек в виде двусвязного списка чанков для каждой из нити и получить с этого немалый профит.

Но я буду четко осознавать (и напишу большими буквами в нескольких местах кода и доки), что это решение будет работать только вот в этих вот условиях. В остальных случаях будет кака-бяка и пичаль.

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

PS. А еще есть http://isocpp.org/
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 29, 2013, 04:24:56 am

Такое - абсолютно корректно. Тут i - это ни разу не указатель.
не понял, в общем случае операция разименования перекрывается или нет?
Да. Можно написать свой класс который будет иметь операцию разименовывания.

Выглядит это как-то так:
struct Foo {
    int operator*(){return 42;}
};

Естественно оператор разименовывания не обязан возвращать именно int.
как переопределяются операторы я знаю.. меня интересуют другой вопрос.. - в самой stl - перекрывается оператор разименования или это дело исключительно обьекта который помещается в контейнер.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 29, 2013, 04:32:45 am
Читаю всё вместе: критику языка (лучшее в сети — Defective C++ или C++ FQA), cppreference, ответы на stackoverflow ну и отладчик с исходниками STL забывать не стоит. Так сказать, для полноты картины  8)

Однако когда речь идёт о коде, я всегда предпочитаю представлять перед собой то, что стоит за абстракциями. Иногда вплоть до команд. Судя по тому, как пишутся чувствительные к производительности вещи, не только у меня подобное восприятие.
желание хакать неистребимо... гораздо труднее приучить себя писать инвариантно относительно особенностей реализации.. - ибо, на первый взгляд , это всегда 100% оверхед и потери в производительности, кроме того всегда приятно переложить часть ответственности на создателей копмилятора и яп (дескать буду использовать адресную арифметику и писать хитрожопые выражения с  инкрементальными операторами ... и все будет за..сь).. дескать они люди заведомо более вумные , коль скоро создали такую похерень, как С++... -так что единомышленниками вы , Berserker, обделены никогда не будете... :).
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 29, 2013, 03:50:08 pm

Такое - абсолютно корректно. Тут i - это ни разу не указатель.
не понял, в общем случае операция разименования перекрывается или нет?
Да. Можно написать свой класс который будет иметь операцию разименовывания.

Выглядит это как-то так:
struct Foo {
    int operator*(){return 42;}
};

Естественно оператор разименовывания не обязан возвращать именно int.
как переопределяются операторы я знаю.. меня интересуют другой вопрос.. - в самой stl - перекрывается оператор разименования или это дело исключительно обьекта который помещается в контейнер.

В stl у каждого типа итераторов перекрыт оператор разименовывания - итератор это отдельный, обобщенный тип (struct обычно), не завязанный вообще говоря, на тип того что там в контейнере будет храниться.

Перекрыто что-то у тех кто будет в контейнерах храниться или нет - никого не волнует.
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 29, 2013, 04:48:12 pm


В stl у каждого типа итераторов перекрыт оператор разименовывания - итератор это отдельный, обобщенный тип (struct обычно), не завязанный вообще говоря, на тип того что там в контейнере будет храниться.

Перекрыто что-то у тех кто будет в контейнерах храниться или нет - никого не волнует.
спасибо, это исчерпывающий ответ, ставящий крест на рассуждениях Берсеркера. Лично я stl не застал -96-98 годах когда приходилось использовать С++, фиг знает почему... отсюда и "странные" вопросы, в особенности  после того как осознал, что сам язык С++ довольно сильно изменился. :D
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 29, 2013, 05:41:11 pm
Цитировать
спасибо, это исчерпывающий ответ, ставящий крест на рассуждениях Берсеркера.
Поясните, если не трудно. Я приводил исключительно факты, которые может проверить каждый. Нет никакой разницы, что у итератора есть оператор разыменования, возвращающий голый указатель, в результате чего ссылка принимает мусорное значение. Частным случаем мусорного значения является нулевой адрес. Зачастую всё гораздо хуже (адрес на стеке или временный блок памяти). Помимо прочего изменение контейнера делает итераторы неверными, но снова без каких-либо проверок времени исполнения. Иначе говоря, итератор, который хранит адрес блока памяти, узла списка или элемента карты и не является устойчивым или защищённым от легального вызова методов, это дырявый итератор. И в STL такие все.

Любой кривой указатель превращается в цивильную ссылку через разыменование. А постоянные отсылки к правильному коду (valid), в котором такие случаи невозможны, из той же оперы, что и просьба не совершать ошибок вообще.

P.S. Если проще, то ссылки не имеют права быть мусорными. А значит если std::vector на запрос элемента через оператор [] по описанию возвращает ссылку, то это должна быть ссылка или выброс исключения. Если итератор стал неверным, при попытке его использования должно тоже бросаться исключение. Ну а реальности всё так же, как и с ручным управлением памяти. Не совершайте ошибок, господа.

P.S.S Два оператора доступа к элементу ([], at) — это просто шутка разработчиков. Объект либо гарантирует безопасность своего использования, либо нет. А предоставление для ограниченного функционала специальной безопасной функции с выбивающимся из общего стиля синтаксисом — это яркий пример неудачного решения.
Название: Re: C++ инициализация ссылок
Отправлено: vlad от Март 29, 2013, 07:04:01 pm
Помимо прочего изменение контейнера делает итераторы неверными, но снова без каких-либо проверок времени исполнения.

Не всякого контейнера. Валидность итераторов и изменение контейнеров явно прописано в стандарте. Так что писать без UB можно, при желании ;)

Иначе говоря, итератор, который хранит адрес блока памяти, узла списка или элемента карты и не является устойчивым или защищённым от легального вызова методов, это дырявый итератор. И в STL такие все.

STL написана в расчете на эффективность (с точки зрения быстродействия). Дырявые итераторы (порой превращающиеся в голые указатели) - прямое следствие такого расчета. Вообще не вижу смысла перетирать еще раз про "безопасность vs эффективность". С++ традиционно небезопасный и быстрый. Безопасность в С++ достигается не низкоуровневыми средствами рантайма (типа GC), а высокоуровневыми под задачу (смарт поинтеры, ненулевые указатели и т.д.). При этом железных гарантий, естественно, нет (внизу голые поинтеры). Но как показывает практика при желании и высокоуровневых гарантий вполне хватает. В частности, STL - попытка подняться над поинтерами и массивами. И еще раз - без потери эффективности. Со своей задачей справляется неплохо, при умении пользоваться.

Любой кривой указатель превращается в цивильную ссылку через разыменование.

Кривой указатель не может превратиться в валидную ссылку. Что за фантазии такие? Кривой указатель - это UB и точка.

P.S. Если проще, то ссылки не имеют права быть мусорными.

Так же как и мусорные указатели не имеют права быть разименованными. И это "право" в С++ поддерживает программист, а не рантайм.

А значит если std::vector на запрос элемента через оператор [] по описанию возвращает ссылку, то это должна быть ссылка или выброс исключения.

Это опять твои фантазии. Для [] прописан UB для невалидного индекса. Что он там возвращает в таком случае уже не важно.

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

Запили свою привильную STL. Кто мешает? Гарантировать валидность смартпоитера  в С++ можно. Так же как и всегда валидный итератор написать. Такая STL будет тормознее обычной и поэтому не очень востребованной. Нафига козе баян? Если можно просто взять другой безопасный язык?

Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 29, 2013, 07:11:52 pm
"безопасный язык" - штука ну о-очень относительная. Вот скажем java, c# не являются безопасными языками с точки зрения haskell'я :-)

А еще понятие безопасности зависит от задачи. Если у меня жесткий реалтайм, то какой-нибудь чертов Си или Асм будет более безопасен чем haskell ;-)

Впрочем, "безопасность по памяти" в C++11 включена же:
http://en.cppreference.com/w/cpp/memory/gc/pointer_safety
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 29, 2013, 07:14:47 pm
Цитировать
спасибо, это исчерпывающий ответ, ставящий крест на рассуждениях Берсеркера.
Поясните, если не трудно.
Да , конечно - мы друг друга не  понимаем... в данном случае крест ставится на "легитимности" рассуждений (но разумеется это имхо)... тут все просто.. если разименование итератора это ОК. то ваши рассуждения были бы легитимными в том случае если бы операция разименования не перекрывалась в итераторах , а раз это не так (пусть даже если это перекрытие будет чисто декларативным - то есть по факту не наблюдаемым в коде) - ваши действия есть  чистый хак (или эксплоит , если вам это больше нравится), и рассуждения могут оказаться неверными в следующей реализации STL. То есть смотрите... я говорю  про то, что ваши рассуждения имеют очевидную уязвимость ВНЕ ЗАВИСИМОСТИ  от того  насколько они верны в приведенных вами примерах....
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Март 29, 2013, 07:33:04 pm
Дизер, я вовсе не полагаюсь на особенности реализации при написании кода. Так что в этом вопросе у нас разногласий нет.

Алексей, хочу заметить, что никаких волшебных опций или макросов для контроля безопасности STL нет.

Влад, скажите, Вы действительно считаете, что правильным библиотекам достаточно указать в описаниях функций и методов о неопределённом поведении (на деле старту развала программы) при неверных аргументах, и всё, проверки не нужны? Вы же сами приводили пример, когда программист не читает документацию. В чём проблема использовать указатель вместо ссылки, не ставить assert, а в документации написать про UB при nullptr?
Название: Re: C++ инициализация ссылок
Отправлено: DddIzer от Март 29, 2013, 07:37:42 pm
Дизер, я вовсе не полагаюсь на особенности реализации при написании кода. Так что в этом вопросе у нас разногласий нет.

если это так - то зачем вообще приводить было этот пример....
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Март 29, 2013, 07:40:31 pm
Алексей, хочу заметить, что никаких волшебных опций или макросов для контроля безопасности STL нет.
В стандарте - нет. В конкретных реализациях - бывает.

Про контроль безопасности по памяти, я в предыдущем посте дал ссылку. Это в стандарте есть безотносительно stl.

И да, я считаю решение с [] и at() в векторе абсолютно адекватным. Неоднократно пользовался и тем и этим (и в виде смеси) и очень был доволен.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Май 24, 2013, 09:54:29 pm
Размышляя на досуге, пришёл к выводу, что большинство типов указателей, как существующих, так и новых, можно описать при помощи ограниченного набора атрибутов. Набор представлен ниже:

const — указатель не изменяемый, инициализация по месту объявления или в качестве аргумента функции;
no_addr — запрещает применение операции получения адреса к указателю; Используется для повышения возможностей оптимизатора.
auto_deref — упоминание идентификатора указателя равнозначно его разыменованию на месте "(*p)". Включает в себя no_addr. Для установки значения самого указателя используется специальный синтаксис (ключевое слово let, инициализация при объявлении в С++, аргумент функции);
fragile — "ломкий". Указатель на данные подлежащие скорому удалению. Семантика перемещения в С++;
not_null — гарантированно указывает на данные, проверка времени компиляции;
null_guard — проверка на неравенство нулевому указателю во время исполнения (в аргументах функций, при присвоении указателей без атрибута null_guard или not_null).

Теперь составим известные нам типы указателей из Ады, Паскаля, Оберона и С++ (pointer — просто указатель):

1) Указатель, к которому нельзя применить операцию взятия адреса:
Код: (cpp) [Выделить]
typedef no_addr pointer ptr;
int x;
int ptr px = &x; // px = @x
&px // error
*px = 7; // можно оптимизировать до x = 7, px выбросить вообще

2) Автоматически разыменовываемый указатель
Код: (cpp) [Выделить]
typedef auto_deref pointer ref;
int x;
int ref px; // px = @???
px = 1; // possible run-time crash
let px = &x; // px = @x
px = 1; // x = 1

3) Гарантированно указывающий на что-либо указатель
Код: (cpp) [Выделить]
typedef not_null pointer ptr;
int x, y;
int ptr p; // compile time error
int ptr p = &x; // p = @x
p = &y; // p = @y
*p = 1; // y = 1

4) Гарантированно ненулевой указатель, проверка времени исполнения
Код: (cpp) [Выделить]
typedef null_guard pointer ptr;

void Test (int ptr p) {}
iTest(nullptr); // run-time error

5) Семантика перемещения — функция принимает аргументом указатель на объект, который будет следом удалён
Код: (cpp) [Выделить]
typedef fragile pointer ptr;
void Test (TObject ptr obj) {}
int *p = new int();
Test(p);
delete p;

6) VAR-параметр Оберона, Паскаля, Ады
Код: (delphi) [Выделить]
typedef const auto_deref not_null pointer var;
procedure Test (var x: integer);
Test(1); // compile-time error
Test(x); // ok
Test(p^); // ok if p is pointer to integer

7) not null аргумент Ады, проверка времени исполнения
Код: (delphi) [Выделить]
typedef not_null pointer ptr;
procedure Test (x: ptr integer);
Test(nil); // compile-time error
Test(@x); // ok
Test(p); // ok

8) Классические ссылки в С++
Код: (cpp) [Выделить]
typedef const not_null auto_deref pointer &;
Примечание: возможность иметь ссылки на константы или возвращаемые по значению объекты есть синтаксический сахар, который можно с тем же успехом применить к любым пользовательским указателям в новой модели:

Код: (cpp) [Выделить]
const int* p = 1; // p = @c1, c1 is const int, c1 = 1
TObject Test () {}
TObject* obj = Test(); // obj = @local1, local1 is TObject, @local1 передаётся функции теневым аргументом

Что думаете, господа?
Название: Re: C++ инициализация ссылок
Отправлено: valexey_u от Май 24, 2013, 09:57:59 pm
Что думаете, господа?
Я глубоко в написанное не вникал, поэтому что думаю сказать не могу. Зато могу посоветовать прочесть про типы "указателей" в Rust'e. Там нечто подобное вроде бы как раз и сделали.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Май 24, 2013, 10:19:05 pm
Пришло время таки взглянуть на Rust.
Название: Re: C++ инициализация ссылок
Отправлено: Berserker от Май 24, 2013, 10:27:34 pm
Не похоже (http://static.rust-lang.org/doc/0.6/rust.html#pointer-types):

Цитировать
8.1.8 Pointer types

All pointers in Rust are explicit first-class values. They can be copied, stored into data structures, and returned from functions. There are four varieties of pointer in Rust:
Managed pointers (@)

These point to managed heap allocations (or "boxes") in the task-local, managed heap. Managed pointers are written @content, for example @int means a managed pointer to a managed box containing an integer. Copying a managed pointer is a "shallow" operation: it involves only copying the pointer itself (as well as any reference-count or GC-barriers required by the managed heap). Dropping a managed pointer does not necessarily release the box it points to; the lifecycles of managed boxes are subject to an unspecified garbage collection algorithm.
Owning pointers (~)

These point to owned heap allocations (or "boxes") in the shared, inter-task heap. Each owned box has a single owning pointer; pointer and pointee retain a 1:1 relationship at all times. Owning pointers are written ~content, for example ~int means an owning pointer to an owned box containing an integer. Copying an owned box is a "deep" operation: it involves allocating a new owned box and copying the contents of the old box into the new box. Releasing an owning pointer immediately releases its corresponding owned box.
Borrowed pointers (&)

These point to memory owned by some other value. Borrowed pointers arise by (automatic) conversion from owning pointers, managed pointers, or by applying the borrowing operator & to some other value, including lvalues, rvalues or temporaries. Borrowed pointers are written &content, or in some cases &f/content for some lifetime-variable f, for example &int means a borrowed pointer to an integer. Copying a borrowed pointer is a "shallow" operation: it involves only copying the pointer itself. Releasing a borrowed pointer typically has no effect on the value it points to, with the exception of temporary values, which are released when the last borrowed pointer to them is released.
Raw pointers (*)

Raw pointers are pointers without safety or liveness guarantees. Raw pointers are written *content, for example *int means a raw pointer to an integer. Copying or dropping a raw pointer is has no effect on the lifecycle of any other value. Dereferencing a raw pointer or converting it to any other pointer type is an unsafe operation. Raw pointers are generally discouraged in Rust code; they exist to support interoperability with foreign code, and writing performance-critical or low-level functions.