Здравствуйте, c-smile, Вы писали:
CS>1) Ножик, он хоть и острый, но вельми полезный бывает. CS>2) Советы не использовать ножики в быту надо сопровождать чеками на соответсвующие суммы позволяющие нанять людей которые будут для тебя точить твои карандаши пока ты творишь.
так ведь есть такие иструменты — тотже QString например (и денег не просит)
и да я буду дальше творить, а не затачиваться на указателях на char
CS>Ну детский сад какой-то право слово. Тебе даден инструмент ты или им пользуйся профессионально или займись уже .NETом каким что-ли. CS>В C/C++ без указателей никак.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, c-smile, Вы писали:
I>
CS>>T const function();
CS>>const T function();
I>
CS>>я правильно понял что общественность настаивает на том что const у возвращаемого значения избыточен?
I>Да, const избыточен в обоих случаях, если T имя типа, а не placeholder.
Что такое placeholder в этом случае? Вот это:
const char* function();
к какой категории относится? placeholder или имя типа?
Далее, скажем вот есть такая конструкция:
class collection
{
...
const value operator[](int i) const { return get_item_at(i); }
}
Я например нахожу const value объявление в данном случае крайне полезным.
То есть твоя "крайне полезная" защита практически бесполезна.
На самом деле нужно передавать и возвращать ссылку на const value, но тогда const нельзя записать непосредственно перед именем функции и правило "const непосредственно перед именем функции избыточен" остается в силе.
И все же есть один случай, когда const в этом месте может иметь смысл, а именно в случае, если возвращается объект proxy-класса, но в этом случае имя этого класса не должно использоваться пользователем (даже если он видит его в файле заголовка и формально может использовать его). Что ж, в C++ из каждого правила есть исключения.
I>То есть твоя "крайне полезная" защита практически бесполезна.
Ты не понял идею.
Объявив функцию так:
void f(value v)
ты явным образом попросил создать копию параметра. С копией не возбраняется делать что угодно.
Если же твоя функция объявлена как
void f(value& v)
То вот это:
collection coll;
f(coll[2]);
вызовет ошибку.
А такая функция:
void f(const value& v)
нет.
I>
I>И все же есть один случай, когда const в этом месте может иметь смысл, а именно в случае, если возвращается объект proxy-класса, но в этом случае имя этого класса не должно использоваться пользователем (даже если он видит его в файле заголовка и формально может использовать его). Что ж, в C++ из каждого правила есть исключения.
Ну т.е. ты больше не настаиваешь на своем утверждении:
"const непосредственно перед именем функции избыточен".
Я правильно тебя понял?
Здравствуйте, igna, Вы писали:
I>Здравствуйте, c-smile, Вы писали:
I>
CS>>T const function();
CS>>const T function();
I>
CS>>я правильно понял что общественность настаивает на том что const у возвращаемого значения избыточен?
I>Да, const избыточен в обоих случаях, если T имя типа, а не placeholder.
Я бы не был так категоричен. В моем понимании "избыточен" означает "ничего не меняет". Т.е. какой-либо элемент избыточен, если от его наличия или отсутствия ничего не меняется. Если же возвращаемым типом функции является тип-класс, то без модификатора const для результата можно вызывать неконстантные методы, а с модификатором const — только константные. Т.е. разница есть, все-таки.
--
Справедливость выше закона. А человечность выше справедливости.
CS>>>T const function();
CS>>>const T function();
I>>
CS>>>я правильно понял что общественность настаивает на том что const у возвращаемого значения избыточен?
I>>Да, const избыточен в обоих случаях, если T имя типа, а не placeholder.
R>Я бы не был так категоричен. В моем понимании "избыточен" означает "ничего не меняет". Т.е. какой-либо элемент избыточен, если от его наличия или отсутствия ничего не меняется. Если же возвращаемым типом функции является тип-класс, то без модификатора const для результата можно вызывать неконстантные методы, а с модификатором const — только константные. Т.е. разница есть, все-таки.
Вот выдержки из стандарта подкрепляющие сказанное:
3.10/5
The result of calling a function that does not return a reference is an rvalue.
3.10/9
Class rvalues can have cv-qualified types; non-class rvalues always have cv-unqualified types.
Т.е. что есть модификатор const при non-class rvalue, что нет — все равно объект будет cv-unqualified, в этом случае модификатор действительно избыточен. А вот для class rvalues наличие const или volatile имеет значение.
--
Справедливость выше закона. А человечность выше справедливости.
rg45:
R>Вот выдержки из стандарта подкрепляющие сказанное: R>
R>3.10/5
R>The result of calling a function that does not return a reference is an rvalue.
R>
R>3.10/9
R>Class rvalues can have cv-qualified types; non-class rvalues always have cv-unqualified types.
R>Т.е. что есть модификатор const при non-class rvalue, что нет — все равно объект будет cv-unqualified, в этом случае модификатор действительно избыточен.
Это правило в 3.10/9 конфликтует с 5.2.2/3 — "The type of the function call expression is the return type of the statically chosen function" — где о снятии с типа возврата cv-qualifier ничего не сказано. Причём VC++ 9.0 и GNU C++ 4.1.2 в этом отношении строго подчиняются именно 5.2.2/3:
int f1();
int const f2();
template <class T>
void g(T &);
int g(double);
int main()
{
sizeof g(f1());
sizeof g(f2()); // VC++ & GNU C++: T is deduced to be 'const int'
}
Также можно отметить, что типы функций f1 и f2 в любом случае разные:
int f1() { return 0; }
int const f2() { return 0; }
int main()
{
int (&r1)() = f1;
int (&r2)() = f2; // ill-formed
}
CS>Т.е. здесь const избыточен? Я правильно следую логике рассуждений?
Нет. Он разве находится "непосредственно перед именем функции" или может быть перемещен туда?
CS>Объявив функцию так:
CS>void f(value v)
CS>ты явным образом попросил создать копию параметра. С копией не возбраняется делать что угодно.
Если существует причина приводящая к необходимости запрета на изменение возвращаемого временного значения (например потому-что значение это является proxy, вызов не const-метода для которого приводит к изменению некоторого другого объекта, изменение которого мы хотели бы предотвратить), то вызов не const-метода копии приведет точно к тому же нежелательному результату. То есть если уж, то конструктор копирования нужно сделать не-public.
CS>Ну т.е. ты больше не настаиваешь на своем утверждении: CS>"const непосредственно перед именем функции избыточен". CS>Я правильно тебя понял?
Нет.
Серьезно, несмотря на неоднозначность такого ответа или возможно как раз благодаря ей. Правило "const непосредственно перед именем функции избыточен" верно в 999 случаях из 1000. Кроме того не надо забывать, что я сформулировал его только для определенного стиля использования const, а именно когда const указывается как можно правее.
Здравствуйте, Николай Ивченков, Вы писали:
R>>Вот выдержки из стандарта подкрепляющие сказанное:
... R>>
R>>3.10/9
R>>Class rvalues can have cv-qualified types; non-class rvalues always have cv-unqualified types.
R>>Т.е. что есть модификатор const при non-class rvalue, что нет — все равно объект будет cv-unqualified, в этом случае модификатор действительно избыточен.
НИ>Это правило в 3.10/9 конфликтует с 5.2.2/3 — "The type of the function call expression is the return type of the statically chosen function" — где о снятии с типа возврата cv-qualifier ничего не сказано. Причём VC++ 9.0 и GNU C++ 4.1.2 в этом отношении строго подчиняются именно 5.2.2/3:
НИ>
int f1();
НИ>int const f2();
НИ>template <class T>
НИ> void g(T &);
НИ>int g(double);
НИ>int main()
НИ>{
НИ> sizeof g(f1());
НИ> sizeof g(f2()); // VC++ & GNU C++: T is deduced to be 'const int'
НИ>}
Я не вижу здесь противоречия. Ведь в качестве аргумента, передаваемого в 'g' выступает некоторое значение, а не тип и не выражение некоторого типа. Но т.к. это значение представляет из себя 'non-class rvalue', то квалификатор с него должен быть снят, и только после этого тип аргумента начнет фигурировать в процессе overload resolution (для 'g').
Юрий Жмеренецкий:
ЮЖ>Ведь в качестве аргумента, передаваемого в 'g' выступает некоторое значение, а не тип и не выражение некоторого типа.
Этот тезис нуждается в обосновании.
1.3.1 argument
an expression in the comma-separated list bounded by the parentheses in a function call expression, a sequence of preprocessing tokens in the comma-separated list bounded by the parentheses in a function-like macro invocation, the operand of throw, or an expression, type-id or template-name in the comma-separated list bounded by the angle brackets in a template instantiation. Also known as an actual argument or actual parameter.
ЮЖ>Но т.к. это значение представляет из себя 'non-class rvalue'
Значения не делятся на lvalue и rvalue. Lvalue и rvalue — это виды выражений:
3.10 Lvalues and rvalues
1 Every expression is either an lvalue or an rvalue.
CS>Здравствуйте, x905, Вы писали:
X>>не совсем в тему, но предлагаю перейти на "классовые" строки типа QString — проблем будет меньше CS> Это совсем не в тему.
CS>Слепое использование QString или std::string в тех местах где можно обойтись просто const wchar_t* или slice<wchar_t> CS>может привести к одной но зело неприятной проблеме — перерасход и/или фрагментация heap.
По моему совет то как раз именно в тему — сишные массивы нужно использовать только когда они действительно необходимы.
И вместо них нужно использовать врапперы (в данном случае std::string) и не только для возврата результата, но и для полей внутри класса.
Преимущества:
— нет проблем с размерами (не нужно заботиться о расширении и выходе за границу массива),
— удобные операции конкатенации, поиска, прохода по контейнеру
Недостатки:
— уже указанная фрагментация кучи процесса и как следствие — тормоза при выделении памяти из кучи.
Отсюда мой вывод — использовать всегда врапперы, пока тесты не начинают показывать что производительность не соответствует требованиям к продукту.
И то в этом случае можно перейти на аллокаторы отдающие память не из кучи а из пула на стеке.
Короче, как говорится:
"
1. сделай чтобы это работало,
2. сделай чтобы это работало правильно
3. и только потом сделай чтобы это работало быстро".
И вообще — берем "Стандарты программирования на C++" Александреску и Саттера и смотрим совет 77 "Вместо массивов используйте vector и string":
Избегайте реализации абстракции массива посредством массивов в стиле C, арифметики указателей и примитивов управления памятью. Использование vector и string не только сделает проще вашу жизнь, но и позволит написать более безопасную и масштабируемую программу.
CS>Если понятие const char* вызывает проблемы то имхо следует уже переходить на .NET/Java. Там во всяком случае не будет фрагментации.
А если:
— плюсы корпоративный стандарт,
— куча кода на C++
— весь third party API — плюсовый
Да и есть ли в .NET/Java такие же удобные алгоритмы и boost как в плюсах?
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, x905, Вы писали:
X>>не совсем в тему, но предлагаю перейти на "классовые" строки типа QString — проблем будет меньше
CS>Это совсем не в тему.
CS>Слепое использование QString или std::string в тех местах где можно обойтись просто const wchar_t* или slice<wchar_t> CS>может привести к одной но зело неприятной проблеме — перерасход и/или фрагментация heap.
std::string и подобные классы как раз и существуют для использования по умолчанию, т.е. именно для слепого использования. Случаи, когда грамотное использование этих классов приводят к проблемам производительности на практике можно считать скорее исключением, чем правилом. А вот слепое использование голых указателей в том месте, где можно использовать умные обертки, может привести к проблемам во сто крат более серьезным — неустойчивость по отношению к исключениям, утечки памяти, что чревато настоящим перерасходом.
CS>Если понятие const char* вызывает проблемы то имхо следует уже переходить на .NET/Java. Там во всяком случае не будет фрагментации.
Не вызывает понятие const char* никаких проблем. Ассемблер, например, тоже не вызывает проблем с пониманием — выбираем ассемблер стандартным инструментом разработки?
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Николай Ивченков, Вы писали:
НИ>Юрий Жмеренецкий:
ЮЖ>>Ведь в качестве аргумента, передаваемого в 'g' выступает некоторое значение, а не тип и не выражение некоторого типа.
НИ>Этот тезис нуждается в обосновании. НИ>
1.3.1 argument
НИ>an expression in the comma-separated list bounded by the parentheses in a function call expression, a sequence of preprocessing tokens in the comma-separated list bounded by the parentheses in a function-like macro invocation, the operand of throw, or an expression, type-id or template-name in the comma-separated list bounded by the angle brackets in a template instantiation. Also known as an actual argument or actual parameter.
Под 'argument passing' понимается не 'expression passing', а инициализация (8.5.3/2). Если initializer expression является rvalue, то объект (параметр) будет инициализирован его значением (8.5/14, возможно косвенно в некоторых случаях). Вот это я имел ввиду (хотя там формулировка не совсем удачная).
В момент инициализации вид выражения является значимым, но в процессе вывода template arguments это не имеет значения (в рассматриваемом примере). Но в 14.8.2.1 сказано следующее:
14.8.2.1/1
Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below
14.8.2.1/2
If P is not a reference type:
...
— If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
Это как раз описывает наш случай.
PS: В первоначальном примере msvc с /Za выводит тип как 'int' и выбирает void g(double).
Юрий Жмеренецкий:
ЮЖ>Под 'argument passing' понимается не 'expression passing', а инициализация (8.5.3/2). Если initializer expression является rvalue, то объект (параметр) будет инициализирован его значением (8.5/14, возможно косвенно в некоторых случаях). Вот это я имел ввиду (хотя там формулировка не совсем удачная).
Как это влияет на вывод шаблонных аргументов?
ЮЖ>Но в 14.8.2.1 сказано следующее: ЮЖ>
14.8.2.1/1
ЮЖ>Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below
ЮЖ>
14.8.2.1/2
ЮЖ>If P is not a reference type:
ЮЖ>...
ЮЖ>— If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
ЮЖ>Это как раз описывает наш случай.
В нашем случае P — это T &, что определённо есть reference type.
Здравствуйте, Николай Ивченков, Вы писали:
НИ>Юрий Жмеренецкий:
ЮЖ>>Под 'argument passing' понимается не 'expression passing', а инициализация (8.5.3/2). Если initializer expression является rvalue, то объект (параметр) будет инициализирован его значением (8.5/14, возможно косвенно в некоторых случаях). Вот это я имел ввиду (хотя там формулировка не совсем удачная).
НИ>Как это влияет на вывод шаблонных аргументов?
Здесь — никак, я уже сказал об этом.
ЮЖ>>Но в 14.8.2.1 сказано следующее: ЮЖ>>
14.8.2.1/1
ЮЖ>>Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below
ЮЖ>>
14.8.2.1/2
ЮЖ>>If P is not a reference type:
ЮЖ>>...
ЮЖ>>— If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
ЮЖ>>Это как раз описывает наш случай.
НИ>В нашем случае P — это T &
Юрий Жмеренецкий:
ЮЖ>>>Под 'argument passing' понимается не 'expression passing', а инициализация (8.5.3/2). Если initializer expression является rvalue, то объект (параметр) будет инициализирован его значением (8.5/14, возможно косвенно в некоторых случаях). Вот это я имел ввиду (хотя там формулировка не совсем удачная).
НИ>>Как это влияет на вывод шаблонных аргументов? ЮЖ>Здесь — никак, я уже сказал об этом.
Тогда я не понимаю, в чём заключается суть твоего возражения с упоминанием значений.
НИ>>В нашем случае P — это T &
ЮЖ>Это неверно.
Если P — это не T &, то что тогда? И заодно хотелось бы увидеть примеры, когда P представляет собой "reference type" или "cv-qualified type", а также когда P записан в виде template-id (в стандарте есть фраза "If P is a class, and P has the form template-id, then [...]").
ЮЖ>Из этого определения следует, что появлению template parametr'a должна предшествовать такая конструкция: 'template <'.
Формулировка "function template parameter type (call it P)", на мой взгляд, выбрана неудачно, но P — это определённо не template parameter и не type of template parameter (в обоих случаях дальше по тексту в стандарте получился бы бред). P — не что иное, как type of function parameter of function template. Надеюсь, то, что следующая программа
struct X {};
X const f();
template <class T>
int g(T &);
int main()
{
sizeof g(f());
}
является well-formed, и то, что здесь шаблонным аргументом является const X, сомнений не вызывает.