Помогите ответь на заданный вопрос. Прошу не спрашивайте откуда взят этот вопрос, а просто помогите ответить.
Какие свойства(семантические и/или синтаксические) есть у "встроенных" операторах, которые отличают их от простоты функций?
Как я понял, то имееться ввиду,например, использование
cout << myObject;
вместо
myObject.print();//или print(myObject);
А какие свойства...?
Большое спасибо.
28.05.10 02:50: Перенесено модератором из 'C/C++. Прикладные вопросы' — Кодт
Это не встроенный оператор, а перегруженный.
Всего лишь инфиксная запись функции ostream& ::operator<<(ostream&, const T&)
либо функции-члена ostream& ostream::operator<<(const T&)
Встроенный << это оператор сдвига для целых чисел.
У функций бывают дефолтные аргументы, а у операторов нет.
У шаблонов функций можно указывать параметры явно: foo<int>(x) и-или выводить из типов аргументов, у шаблонов операторов параметры выводятся.
Оператор-член и оператор-свободная функция выглядят одинаково: cout << x. Естественно, что если оператор-член, то объект — это его левый аргумент.
У операторов немножко другой порядок поиска перегрузок (argument-dependent lookup), чем у функций. Об этом лучше у отцов-основателей почитать, у Саттера, кажется, было разжёвано.
Здравствуйте, Кодт, Вы писали:
К>Какие ещё свойства интересуют?
Честно, то я не знаю какие еще свойства. Я надейлся здесь получить ответа.
Буду рад за помощь.
Здравствуйте, TheAteist, Вы писали:
К>>Какие ещё свойства интересуют? TA>Честно, то я не знаю какие еще свойства. Я надейлся здесь получить ответа. TA>Буду рад за помощь.
Чтобы не отвечать наугад, — лучше всё-таки расскажи, откуда взялся вопрос.
Здравствуйте, Кодт, Вы писали:
К>У функций бывают дефолтные аргументы, а у операторов нет. К>У шаблонов функций можно указывать параметры явно: foo<int>(x) и-или выводить из типов аргументов, у шаблонов операторов параметры выводятся. К>Оператор-член и оператор-свободная функция выглядят одинаково: cout << x. Естественно, что если оператор-член, то объект — это его левый аргумент. К>У операторов немножко другой порядок поиска перегрузок (argument-dependent lookup), чем у функций. Об этом лучше у отцов-основателей почитать, у Саттера, кажется, было разжёвано.
Ещё у операторов приоритеты разные, зависит от того, через какой именно оператор мы реализовали функциональность. А для функций приоритет всегда один.
Здравствуйте, Кодт, Вы писали:
К>Какие ещё свойства интересуют?
А еще для операторов определен порядок вычисления аргументов, для логических операторов — "логическое К.З.". Е.М.Н.И.П., в best-practices от Майерса именно по этой причине не рекомендуется перегружать операторы "запятая" и логические операторы.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, TheAteist, Вы писали:
TA>Честно, то я не знаю какие еще свойства. Я надейлся здесь получить ответа. TA>Буду рад за помощь.
Перегруженный оператор удобнее использовать для вывода в любой поток, не только в cout (например в ostringstream).
Кроме этого, Вы можете использовать оператор для вывода в поток контейнера элементов с помошью
ostream_iterator.
Здравствуйте, slava_phirsov, Вы писали:
_>А еще для операторов определен порядок вычисления аргументов, для логических операторов — "логическое К.З.". Е.М.Н.И.П., в best-practices от Майерса именно по этой причине не рекомендуется перегружать операторы "запятая" и логические операторы.
Порядок определён для встроенных операторов «&&», «||» и «,». А для перегруженных — не определён, равно как и для любых других функций.
Это вносит визуальную сумятицу, поскольку два внешне однообразных выражения работают по-разному.
Оператор «,» отличается ещё и тем, что
— встроенный оператор может получать тип void
— он всеяден — там, где с другим оператором была бы ошибка компиляции (нет подходящей сигнатуры), с запятой всегда наготове встроенный оператор
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, slava_phirsov, Вы писали:
_>>А еще для операторов определен порядок вычисления аргументов, для логических операторов — "логическое К.З.". Е.М.Н.И.П., в best-practices от Майерса именно по этой причине не рекомендуется перегружать операторы "запятая" и логические операторы.
К>Порядок определён для встроенных операторов «&&», «||» и «,». А для перегруженных — не определён, равно как и для любых других функций. К>Это вносит визуальную сумятицу, поскольку два внешне однообразных выражения работают по-разному.
К>Оператор «,» отличается ещё и тем, что К>- встроенный оператор может получать тип void К>- он всеяден — там, где с другим оператором была бы ошибка компиляции (нет подходящей сигнатуры), с запятой всегда наготове встроенный оператор
Спасибо.
Просто есть курс "Языки программирования"(не в России) и был задан вот такой вопрос.
Если есть еще что-то, буду блогадарен.
Здравствуйте, Кодт, Вы писали:
К>Порядок определён для встроенных операторов «&&», «||» и «,». А для перегруженных — не определён, равно как и для любых других функций. К>Это вносит визуальную сумятицу, поскольку два внешне однообразных выражения работают по-разному.
Под порядком подразумевается порядок вычисления аргументов, а не приоритет операторов, я надеюсь?
Чтобы не было сумятицы, нужно писать код, который бы не зависел от порядка вычисления аргументов, то есть без побочных эффектов.
Здравствуйте, Aleх, Вы писали:
A>Под порядком подразумевается порядок вычисления аргументов, а не приоритет операторов, я надеюсь?
Естественно. И это, кстати, ещё одна боль для программистов-новичков. Причём не только с операторами — а даже с вызовом функций.
A>Чтобы не было сумятицы, нужно писать код, который бы не зависел от порядка вычисления аргументов, то есть без побочных эффектов.
Твои бы слова да богу в уши
Здравствуйте, Aleх, Вы писали:
A>Чтобы не было сумятицы, нужно писать код, который бы не зависел от порядка вычисления аргументов, то есть без побочных эффектов.
Увы, иногда Париж стоит мессы. Пример:
if (some_string.size() && some_string[0])
{
...
}
ИМХО гораздо лучше, чем:
if (some_string.size())
if some_string[0])
{
...
}
В первом случае мы используем "логическое К.З.". Если бы порядок вычисления аргументов оператора && был неопределен, или всегда требовалось бы вычислять оба аргумента, то можно было бы использовать только 2-й вариант. Это, конечно, мое личное ИМХО
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, Aleх, Вы писали:
A>Чтобы не было сумятицы, нужно писать код, который бы не зависел от порядка вычисления аргументов, то есть без побочных эффектов.
P.S. Вот например такой код 100% зависит от порядка вычисления аргументов, и обойтись без этого ээээээ довольно проблематично:
foo = bar(foo);
"=" тоже является оператором, как ни странно, причем его тоже можно перегрузить... А foo, как ты, конечно, заметил, содержится в обеих частях выражения
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
_>"=" тоже является оператором, как ни странно, причем его тоже можно перегрузить... А foo, как ты, конечно, заметил, содержится в обеих частях выражения
Неудачный пример.
Перегрузить = можно только как член класса, и тогда слева ссылка. Либо это встроенный оператор, и тогда опять слева ссылка.
Адрес объекта не меняется из-за операций над ним, поэтому нам всё равно, что произошло раньше — взяли this=&foo или value=bar(foo), перед тем, как применить operator=(this,value).
И если bar — это вызов функции, то возникают точки следования на входе и выходе из неё, поэтому никакого undefined behavior быть не может, а почву для unspecified behavior мы не сделали.
Вот если bar — это макрос, развёртывающийся в foo++ какое-нибудь, и всё это со встроенными операторами, — то здесь мы точно отхватим UB.
Но, если вместо = написать += (или любой другой), который можно перегружать как свободную функцию... Вот там можно передавать левый аргумент и по значению, и как угодно.
Это даст нам unspecified behavior (аукнется в тех случаях, когда компилятор решит оптимизировать конвенцию cdecl и вычислить левый аргумент раньше правого).
Вот пример, который хорошо оптимизируется и, как следствие, крайне чувствителен к порядку вычислений.
#include <iostream>
using namespace std;
enum V { vmin=INT_MIN, vmax=INT_MAX };
struct X
{
int n;
explicit X(int n) : n(n) {}
operator V() const { return V(n); }
};
V operator + (V a, V b)
{
cout << int(a) << "+" << int(b) << "=" << (int(a)+int(b)) << endl;
return V(int(a)+int(b));
}
V bar(X& x)
{
cout << "bar(" << x.n << ")";
x.n += 100;
cout << "=" << x.n << endl;
return x;
}
int main()
{
X foo(10);
V v = foo + bar(foo) + bar(foo) + bar(foo);
cout << int(v);
}