Здравствуйте, nen777w, Вы писали:
N>Смотрю чужой код... N>Не пойму одну вещ какой смысл объявлять передачу по ссылке в ф-ции для переменных интегральных типов и выведенных из них (typedef), то же вопрос касается передачи по константному указателю (не принимая во внимания того что можно сделать const_cast). N>Компилятор как то особо обрабатывает такие объявления?
Есть 3 способа передавать аргументы в функцию:
1). по значению;
2). по ссылке;
3). по указателю.
В 1-м случае создаётся временный объект, в который копируется значение аргумента, и функция работает с временным объектом. Внутри функции значения переданных аргументов можно изменять, это изменение никак не отразится за пределами функции. Недостаток — непроизводительные потери на создание временного объекта и копирование данных, что может быть достаточно накладно при больших объёмах.
Во 2-м случае функция получает адрес объекта и работает с ним напрямую, поэтому изменение значения аргумента, переданного функции по ссылке, будут видны за пределами функции. Это можно устранить, объявив ссылку константной. Достоинство — не происходит создания временного объекта и копирования даннных.
В 3-м случае почти то же самое, что и во 2-м, передаётся только указатель на данные, создания временного объекта не происходит. Отличия — указатель может быть NULL, при обращении к данным требуется операция разыменования (косвенная адресация).
Здравствуйте, night beast, Вы писали:
NB>теоретически Александреску может и прав, но компиляторы должны уметь оптимизировать это дело. NB>и не известно, какой вариант более предпочтителен для оптимизации.
Передача по значению всё-таки предпочтительнее.
Пример:
В случае foo компилятор может установить, что на n (как и на i) нет никакого влияния извне, и сделать цикл от 0 до n так, как удобнее (например, с обратным отсчётом)
В случае bar таких гарантий нет (возможна ситуация, как xxx + main под чертой), и поэтому нужно перечитывать n на каждой итерации.
Здравствуйте, Nuzhny, Вы писали:
N>Здравствуйте, Erlond, Вы писали:
E>>Я думаю, что компилятор просто соптимизировал ассемлерный код, этот пример достаточно прост. Кстатии вполне вероятно, что переменной к вообще не создаётся, компилятор заменил её на константу, т.к. она не изменяет своего значения.
N>Что значин "не изменяет своего значения"? Изменяет и ещё как (пример, вроде, достаточно простой, чтобы в нём разобраться). Она существует и создаётся.
N>Я, вообще, всегда думал, что передача по ссылке и по указателю физически представляет собой одно и тоже. Разница только в синтаксисе.
Согласен, изменяет, это я спросони напутал.
Передача аргумента по ссылке и по указателю — это разные вещи. Ссылка это просто адрес объекта, а указатель — это "ячейка", где хранится адрес объекта.
Здравствуйте, np9mi7, Вы писали:
N>Здравствуйте, Константин Л., Вы писали:
N>>>Тратиться дополнительное время на один уровень косвенности
КЛ>>это ты о чем?
N>Это об операции разъименования (получения значения по ссылке);
Ссылки не разименовываются, разыменовываются указатели, а ссылка — это просто просто "псевдоним", другое название той же переменной. Ссылка представляет собой адрес в памяти, начиная с которого лежат данные.
Смотрю чужой код...
Не пойму одну вещ какой смысл объявлять передачу по ссылке в ф-ции для переменных интегральных типов и выведенных из них (typedef), то же вопрос касается передачи по константному указателю (не принимая во внимания того что можно сделать const_cast).
Компилятор как то особо обрабатывает такие объявления?
Здравствуйте, nen777w, Вы писали:
N>Смотрю чужой код... N>Не пойму одну вещ какой смысл объявлять передачу по ссылке в ф-ции для переменных интегральных типов и выведенных из них (typedef), то же вопрос касается передачи по константному указателю (не принимая во внимания того что можно сделать const_cast). N>Компилятор как то особо обрабатывает такие объявления?
а что собенного в том, что интегральные типы передаются по ссылке? Может быть ты имел ввиду const&? По поводу const*. Это похоже на опциональный аргумент. Можно и NULL передать.
Здравствуйте, Константин Л., Вы писали:
КЛ>а что собенного в том, что интегральные типы передаются по ссылке?
Тратиться дополнительное время на один уровень косвенности, а памяти отъедает меньше чем фактический тип только в случае long long;
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
КЛ>а что собенного в том, что интегральные типы передаются по ссылке? Может быть ты имел ввиду const&?
Да. Именно так, забыл указать. Если учеть то что кастов никаких с этой переменной в этом коде не проделывают.
КЛ>По поводу const*. Это похоже на опциональный аргумент. Можно и NULL передать.
Да, как то я об этом не подумал, просто сбило то что реально в коде этой ф-ции не допускают её работу с NULL.
Здравствуйте, np9mi7, Вы писали:
КЛ>>а что собенного в том, что интегральные типы передаются по ссылке?
N>Тратиться дополнительное время на один уровень косвенности, а памяти отъедает меньше чем фактический тип только в случае long long;
Здравствуйте, np9mi7, Вы писали:
N>Здравствуйте, Константин Л., Вы писали:
КЛ>>а что собенного в том, что интегральные типы передаются по ссылке?
N>Тратиться дополнительное время на один уровень косвенности, а памяти отъедает меньше чем фактический тип только в случае long long;
Здравствуйте, nen777w, Вы писали:
КЛ>>а что собенного в том, что интегральные типы передаются по ссылке? Может быть ты имел ввиду const&? N>Да. Именно так, забыл указать. Если учеть то что кастов никаких с этой переменной в этом коде не проделывают.
лишняя попытка оптимизации?
КЛ>>По поводу const*. Это похоже на опциональный аргумент. Можно и NULL передать. N>Да, как то я об этом не подумал, просто сбило то что реально в коде этой ф-ции не допускают её работу с NULL.
тогда не вижу смысла. еще одна лишняя попытка оптимизации?
КЛ>>>а что собенного в том, что интегральные типы передаются по ссылке? Может быть ты имел ввиду const&? N>>Да. Именно так, забыл указать. Если учеть то что кастов никаких с этой переменной в этом коде не проделывают. КЛ>лишняя попытка оптимизации? КЛ>>>По поводу const*. Это похоже на опциональный аргумент. Можно и NULL передать. N>>Да, как то я об этом не подумал, просто сбило то что реально в коде этой ф-ции не допускают её работу с NULL. КЛ>тогда не вижу смысла. еще одна лишняя попытка оптимизации?
Ну так это я вопрос задал ок. спишем на какую то мега задумку код писавшего или на невысыпон, вот как у меня сегодня прям
Здравствуйте, night beast, Вы писали:
N>>Тратиться дополнительное время на один уровень косвенности, а памяти отъедает меньше чем фактический тип только в случае long long;
NB>какие нибудь цифры, поддверждающие предположение имеются?
В своей практике ни разу не встречал что замена функции вида
void f (const some_fundamental_type & value);
на
void f (some_fundamental_type value);
давала ощутимый прирост производительности. Но опираясь на классиков Andrei Alexandrescu хочу процитировать отрывок из его книги:
Modern C++ Design: Generic Programming and Design Patterns Applied
Chapter 2. Techniques
2.10.3 Optimized Parameter Types
In template code, you sometimes need to answer the following question: Given an arbitrary type T, what is
the most efficient way of passing and accepting objects of type T as arguments to functions? In general, the
most efficient way is to pass elaborate types by reference and scalar types by value. (Scalar types consist of
the arithmetic types described earlier as well as enums, pointers, and pointers to members.) For elaborate
types you avoid the overhead of an extra temporary (constructor-plus-destructor calls), and for scalar types
you avoid the overhead of the indirection resulting from the reference.
, так что не знаю кому верить: небольшому опыту или классикам. Для себя решил — нет константных ссылок на фундаментальные типы;
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
Здравствуйте, Константин Л., Вы писали:
N>>Тратиться дополнительное время на один уровень косвенности
КЛ>это ты о чем?
Это об операции разъименования (получения значения по ссылке);
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
Здравствуйте, np9mi7, Вы писали:
N>>>Тратиться дополнительное время на один уровень косвенности, а памяти отъедает меньше чем фактический тип только в случае long long;
NB>>какие нибудь цифры, поддверждающие предположение имеются?
N>В своей практике ни разу не встречал что замена функции вида N>
N>void f (const some_fundamental_type & value);
N>
на N>
N>void f (some_fundamental_type value);
N>
давала ощутимый прирост производительности. Но опираясь на классиков Andrei Alexandrescu хочу процитировать отрывок из его книги: N>так что не знаю кому верить: небольшому опыту или классикам. Для себя решил — нет константных ссылок на фундаментальные типы;
верить надо цифрам тестов
теоретически Александреску может и прав, но компиляторы должны уметь оптимизировать это дело.
и не известно, какой вариант более предпочтителен для оптимизации.
Здравствуйте, Erlond, Вы писали:
E>Ссылки не разименовываются, разыменовываются указатели, а ссылка — это просто просто "псевдоним", другое название той же переменной. Ссылка представляет собой адрес в памяти, начиная с которого лежат данные.
Почему тогда дизассемблер показывает две абсолютно идентичные функции:
void f(int *i)
{
*i = 5;
}
void f(int &i)
{
i = 6;
}
int main()
{
int k(0);
f(k);
f(&k);
return 0;
}
Я думаю, что компилятор просто соптимизировал ассемлерный код, этот пример достаточно прост. Кстатии вполне вероятно, что переменной к вообще не создаётся, компилятор заменил её на константу, т.к. она не изменяет своего значения.
Здравствуйте, Erlond, Вы писали:
E>Я думаю, что компилятор просто соптимизировал ассемлерный код, этот пример достаточно прост. Кстатии вполне вероятно, что переменной к вообще не создаётся, компилятор заменил её на константу, т.к. она не изменяет своего значения.
Что значин "не изменяет своего значения"? Изменяет и ещё как (пример, вроде, достаточно простой, чтобы в нём разобраться). Она существует и создаётся.
Я, вообще, всегда думал, что передача по ссылке и по указателю физически представляет собой одно и тоже. Разница только в синтаксисе.
Здравствуйте, Erlond, Вы писали:
E>Здравствуйте, nen777w, Вы писали:
E>В 3-м случае почти то же самое, что и во 2-м, передаётся только указатель на данные, создания временного объекта не происходит. Отличия — указатель может быть NULL, при обращении к данным требуется операция разыменования (косвенная адресация).
Ссылка технически имеет семантику указателя, так что косвенность все равно есть. Вот выше товарищ про дизасемблер писал. Но у нее есть, так сказать, свойства в языке: 1) должна быть инициализирована, 2) указатель может изменить адрес объекта на котрый указаывает, а ссылка нет (точнее, изменится объект):
Ну и синтаксис конечно. Ссылки не инициализипуются (синтаксически) если они параметры функции. Введение их — стремление немного обезопасить C++. Врядли они сильно помогают при оптимизации, кроме как cons T& в ситауциях типа:
class T
{
//Pretty heavy definition
};
void f1(const T* p);
void f2(const T& p);
void f()
{
f1(&T()); //Error
f2( T()); //Ok
}
Не стыдно попасть в дерьмо, стыдно в нём остаться!
Здравствуйте, np9mi7, Вы писали:
N>Здравствуйте, Константин Л., Вы писали:
N>>>Тратиться дополнительное время на один уровень косвенности
КЛ>>это ты о чем?
N>Это об операции разъименования (получения значения по ссылке);
ссылку, подтверждающую слова и тот факт, что копирование дешевле этой операции (если она действительно сущ.)?
[]
К>В случае foo компилятор может установить, что на n (как и на i) нет никакого влияния извне, и сделать цикл от 0 до n так, как удобнее (например, с обратным отсчётом) К>В случае bar таких гарантий нет (возможна ситуация, как xxx + main под чертой), и поэтому нужно перечитывать n на каждой итерации.
Это может произойти только в многопоточном окружении. Разве компилятор должен рассматривать такой случай?
Здравствуйте, Кодт, Вы писали:
NB>>теоретически Александреску может и прав, но компиляторы должны уметь оптимизировать это дело. NB>>и не известно, какой вариант более предпочтителен для оптимизации.
К>Передача по значению всё-таки предпочтительнее. К>Пример: К>В случае foo компилятор может установить, что на n (как и на i) нет никакого влияния извне, и сделать цикл от 0 до n так, как удобнее (например, с обратным отсчётом) К>В случае bar таких гарантий нет (возможна ситуация, как xxx + main под чертой), и поэтому нужно перечитывать n на каждой итерации.
Пример не совсем корректен, поскольку функции делают разные вещи, однако идею уловил. Спасибо.
В реальной ситуации по прежнему больше доверяю тестам
Здравствуйте, Erlond, Вы писали:
E>Есть 3 способа передавать аргументы в функцию:
E>1). по значению; E>2). по ссылке; E>3). по указателю.
E>В 1-м случае создаётся временный объект, в который копируется значение аргумента, и функция работает с временным объектом. Внутри функции значения переданных аргументов можно изменять, это изменение никак не отразится за пределами функции. Недостаток — непроизводительные потери на создание временного объекта и копирование данных, что может быть достаточно накладно при больших объёмах.
E>Во 2-м случае функция получает адрес объекта и работает с ним напрямую, поэтому изменение значения аргумента, переданного функции по ссылке, будут видны за пределами функции. Это можно устранить, объявив ссылку константной. Достоинство — не происходит создания временного объекта и копирования даннных.
E>В 3-м случае почти то же самое, что и во 2-м, передаётся только указатель на данные, создания временного объекта не происходит. Отличия — указатель может быть NULL, при обращении к данным требуется операция разыменования (косвенная адресация).
Врменный объект создаётся всегда. Просто во втором и третем случаях временный объект — указатель. Соответственно, если размер объекта не больше размера указателя, то время на передачу одинаковое.
Во втором и третьем случае разыменование происходит одинакого.
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, np9mi7, Вы писали:
КЛ>>>а что собенного в том, что интегральные типы передаются по ссылке?
N>>Тратиться дополнительное время на один уровень косвенности, а памяти отъедает меньше чем фактический тип только в случае long long;
NB>какие нибудь цифры, поддверждающие предположение имеются?
#include <iostream>
using namespace std;
int foo(int n){
if (n > 1)
return n * foo(n - 1);
else
return 1;
}
int boo(const int& n){
if (n > 1)
return n * boo(n - 1);
else
return 1;
}
int main(int argc, char** argv){
cout << "sizeof(int): " << sizeof(int) << endl;
cout << "sizeof(const int&): " << sizeof(const int&) << endl;
if (argc == 2){
if (argv[1][0] == '1'){
cout << "by value" << endl;
for (int i = 0; i < 10000000; i++)
foo(30);
}
if (argv[1][0] == '2'){
cout << "by const &" << endl;
for (int i = 0; i < 10000000; i++)
boo(30);
}
}
}
moriarty@earthquake:~$ time ./a.out 1
sizeof(int): 4
sizeof(const int&): 4
by value
real 0m2.705s
user 0m2.704s
sys 0m0.004s
moriarty@earthquake:~$ time ./a.out 2
sizeof(int): 4
sizeof(const int&): 4
by const &
real 0m3.056s
user 0m3.048s
sys 0m0.008s
moriarty@earthquake:~$ gcc --version
gcc (GCC) 4.0.3 (Ubuntu 4.0.3-1ubuntu5)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Здравствуйте, Константин Л., Вы писали:
КЛ>Это может произойти только в многопоточном окружении.
Неправда. Мой пример однопоточный, и в нём всё происходит.
КЛ> Разве компилятор должен рассматривать такой случай?
Компилятор не имеет права оптимизировать работу с переменными, которые могут быть доступны под другим именем.
Вот ещё один пример
int x = 0;
int *p = &x;
int i, n; // в этом месте переменные i и n строго локальныfor(i=0, n=10; i!=n; ++i) // и поэтому их можно разместить в регистрах
--*p; // а то и вообще развернуть циклif(rand()%2)
p = &n;
// начиная с этого места n может меняться извнеfor(i=0, n=10; i!=n; ++i)
--*p;
R>Врменный объект создаётся всегда. Просто во втором и третем случаях временный объект — указатель. Соответственно, если размер объекта не больше размера указателя, то время на передачу одинаковое.
Время работы конструктора намного больше, чем время пересылки байтов. Поэтому double лучше передавать по значению, а CString — по ссылке, несмотря на то, что ее размер равен размеру указателя.
R>Во втором и третьем случае разыменование происходит одинакого.
Здравствуйте, HiSH, Вы писали:
КЛ>>>>а что собенного в том, что интегральные типы передаются по ссылке?
N>>>Тратиться дополнительное время на один уровень косвенности, а памяти отъедает меньше чем фактический тип только в случае long long;
NB>>какие нибудь цифры, поддверждающие предположение имеются?
HSH>moriarty@earthquake:~$ time ./a.out 2
HSH>real 0m3.056s
HSH>user 0m3.048s
HSH>sys 0m0.008s
HSH>"И все-таки, она вертится"
угу
собственно, мой поинт был в том, что не стоит безоговорочно верить всему что говорят.
например, передача плейсхолдера по ссылке может быть более быстрой чем передача по значению.
по ситуации смотреть надо.