operator++ (Как им пользоваться???)

Модератор: Модераторы разделов

Аватара пользователя
kkkggg
Сообщения: 100

operator++

Сообщение kkkggg »

#include <iostream.h>
int main ()
{
enum m {a,f,h,j,y,t};
m jj = a;
jj++;
return 0;
}

Ругается:

1.cpp: In function `int main()':
1.cpp:6: error: no `operator++(int)' declared for postfix `++', trying prefix
operator instead
1.cpp:6: error: no match for 'operator++' in '++jj'

Допишите пожалуйста пример...., я не могу понять синтаксис operator-a.
Спасибо сказали:
Аватара пользователя
Asgard
Сообщения: 215
Статус: North Valfader

Re: operator++

Сообщение Asgard »

а что вы, собственно, ожидали, применяя оператор ++ к enum'у?
enum фактически является перечислением констант.

что же касается операторов:

Код: Выделить всё

class SomeClass {
private:
    int member;
   /*...*/
public:
   /*...*/
   SomeClass(): member(0) {}
   void operator++() { ++member; } // prefix
   SomeClass& operator++(int) { return ++(*this);  } // postfix
   /*...*/
};
sator arepo tenet opera rotas ;)
------------------------------------------------------------
LJ
Спасибо сказали:
ToniX
Сообщения: 11

Re: operator++

Сообщение ToniX »

Компилятор совершенно справедливо указывает на ошибку, т. к для enum'ов по умолчанию не определены операторы ++/--. И это не случайно: enum - встроенный тип, по сути представляющий собой конечное множество именованных целочисленных констант. Т. к. множество не только конечно, но и, в общем случае, не последовательно (enum { a = 0, b = 100, c = 1000 }), то компилятор не может сгенерировать быстрый и эффективный код для операторов ++/--. Можно конечно для некоторых частных случаев извратиться на подобии:

Код: Выделить всё

enum SomeEnum { first = 0, second, third, last };

// prefix
SomeEnum &operator ++(SomeEnum &se)
{
  if( se == last ) {
    se = first;
  }
  else {
    int proxy = se;
    ++proxy;
    se = *reinterpret_cast<SomeEnum *>(&proxy);
  }

  return se;
}

// postfix
SomeEnum &operator ++(SomeEnum &se, int)
{
  SomeEnum old = se;
  ++se;

  return old;
}

Но польза в таких операторах видеться мне сомнительной; да и само применение reinterpret_cast давольно красноречиво говорит о “чистоте” подобного подхода. Использование такого решения – верный способ нажить себе неприятностей при сопровождении и расширении программы.

Так что, если всё же возникла необходимость в подобной функциональности, то IMHO лучше обернуть enum в класс, и работать с операторами ++/-- уже на уровне класса.


Извеняюсь, для postfix-формы оператора ++ следовало было возвращать не ссылку, а значение:

Код: Выделить всё

// postfix
SomeEnum operator ++(SomeEnum &se, int)
{
  SomeEnum old = se;
  ++se;

  return old;
}
Спасибо сказали:
Аватара пользователя
GRS
Сообщения: 236
Статус: C++ Pro
ОС: Suse10.2/XP

Re: operator++

Сообщение GRS »

Операторы ++, -- в основном применяются для указателей и счетчиков в циклах.
Для указателей это хорошо тем, что нет необходимости вычислять адрес по индексу массива. Конечно есть минус нету произвольного доступа.
Вторая цель использования счетчика в циклах. Просто, удобно и красиво. Отметил бы только то, что:

Код: Выделить всё

for (int i = 0; i < ANY_SIZE; ++i)

Будет работать быстрее, чем:

Код: Выделить всё

for (int i = 0; i < ANY_SIZE; i++)

Т.к. ++i не возвращает временный объект.
Допишите пожалуйста пример...., я не могу понять синтаксис operator-a.

Простой пример, объясняющий принцип работы операторов ++ и --.

Код: Выделить всё

int i = 4, j = 7;
int res = ++i;    // res = 5. i = 5
res = j--;          // res = 7, j = 6
res = i++;        // res = 5, i = 6
res = --j;          // res = 5, j = 5
Спасибо сказали:
Аватара пользователя
elide
Бывший модератор
Сообщения: 2421
Статус: Übermensch
ОС: лялих

Re: operator++

Сообщение elide »

Т.к. ++i не возвращает временный объект.
компиляторы сейчас не дураки пошли..... если этот самый временный объект не используется, то он и не возвращается. так что, и для

Код: Выделить всё

for (int i = 0; i < ANY_SIZE; ++i)
и для

Код: Выделить всё

for (int i = 0; i < ANY_SIZE; i++)
набор инструкций оказывается тот-же даже в самом простом случае.
слава роботам!
Спасибо сказали:
ToniX
Сообщения: 11

Re: operator++

Сообщение ToniX »

компиляторы сейчас не дураки пошли..... если этот самый временный объект не используется, то он и не возвращается. так что, и для

Код: Выделить всё

for (int i = 0; i < ANY_SIZE; ++i)
и для

Код: Выделить всё

for (int i = 0; i < ANY_SIZE; i++)

набор инструкций оказывается тот-же даже в самом простом случае.


Для простейших случаев это, возможно, и сработает (да и то не факт), но, например, как быть со следующим примером:

Код: Выделить всё

#include <vector>

typedef std::list<int> MyList;

/*
      ......
*/

MyList l;

UseMyList(l);

for( MyList::iterator it = l.begin(); it != l.end(); it++ ) // ???????
  /* .... */;


Компилятор должен создать как минимум одну копию итератора (он может оптимизировать возврат значения, и вместо двух копий создать одну), так как создание класса – уже есть его использование и код будет менее эффективным (возможно значительно), чем в случае ++it. А использовать разные формы оператора ++, в зависимости от "сложности" используемой конструкции IMHO плохой стиль, причем ничем не оправданный. Поэтому я всегда использую префиксную форму, за исключением некоторых (довольно редких) случаев, когда постфиксный оператор обеспечивает более наглядный, короткий и понятный код.
Спасибо сказали:
Аватара пользователя
elide
Бывший модератор
Сообщения: 2421
Статус: Übermensch
ОС: лялих

Re: operator++

Сообщение elide »

ToniX
а что тебе мешает не задрачивать мозг, а взять и попробовать?
даю, типа, хинт: у g++ есть опция -S, при указании которой, он собственно и компилирует указанный файл.
итак, имеем:

Код: Выделить всё

elide@rebirth| ~/exp/c/test/increment >> cat prefix.cpp
#include <list>
#include <iostream>
using namespace std;
int main(){
        list<int> l;
        l.push_back(5);
        l.push_back(6);
        for( list<int>::iterator it = l.begin(); it != l.end(); ++it )
                cout << *it << endl;
        return 0;
}

Код: Выделить всё

elide@rebirth| ~/exp/c/test/increment >> diff prefix.cpp postfix.cpp
8c8
<       for( list<int>::iterator it = l.begin(); it != l.end(); ++it )
---
>       for( list<int>::iterator it = l.begin(); it != l.end(); it++ )

Код: Выделить всё

elide@rebirth| ~/exp/c/test/increment >> g++ -S prefix.cpp
elide@rebirth| ~/exp/c/test/increment >> g++ -S postfix.cpp
elide@rebirth| ~/exp/c/test/increment >> diff prefix.s postfix.s
1c1
<       .file   "prefix.cpp"
---
>       .file   "postfix.cpp"
elide@rebirth| ~/exp/c/test/increment >>
итого: одна единственная строчка различий в названии файлов.
слава роботам!
Спасибо сказали:
ToniX
Сообщения: 11

Re: operator++

Сообщение ToniX »

elide писал(а):
23.05.2006 20:27
ToniX
а что тебе мешает не задрачивать мозг, а взять и попробовать?
даю, типа, хинт: у g++ есть опция -S, при указании которой, он собственно и компилирует указанный файл.
итак, имеем:

Код: Выделить всё

elide@rebirth| ~/exp/c/test/increment >> cat prefix.cpp
#include <list>
#include <iostream>
using namespace std;
int main(){
        list<int> l;
        l.push_back(5);
        l.push_back(6);
        for( list<int>::iterator it = l.begin(); it != l.end(); ++it )
                cout << *it << endl;
        return 0;
}

Код: Выделить всё

elide@rebirth| ~/exp/c/test/increment >> diff prefix.cpp postfix.cpp
8c8
<       for( list<int>::iterator it = l.begin(); it != l.end(); ++it )
---
>       for( list<int>::iterator it = l.begin(); it != l.end(); it++ )

Код: Выделить всё

elide@rebirth| ~/exp/c/test/increment >> g++ -S prefix.cpp
elide@rebirth| ~/exp/c/test/increment >> g++ -S postfix.cpp
elide@rebirth| ~/exp/c/test/increment >> diff prefix.s postfix.s
1c1
<       .file   "prefix.cpp"
---
>       .file   "postfix.cpp"
elide@rebirth| ~/exp/c/test/increment >>
итого: одна единственная строчка различий в названии файлов.


Во-первых, мир состоит не только из компилятора gcc и твоей платформы. Во-вторых, оператор ++ применяется далеко не только к встроенным типам и итераторам стандартных контейнеров. И что, для каждого контейнера или класса, попавшего тебе в руки, ты будешь проверять, сможет ли твой ДАННЫЙ компилятор, на ДАННОЙ платформе сгенерировать одинаково эффективный код для двух версий оператора ++?

Чтобы не быть голословным, я взял компилятор gcc-3.4.2 под Windows XP (MinGW) и прогнал твои примеры. Результат таков: 1187 строк дизассемблерного листинга prefix против 1192 postfix. Основные различия, как и следовало ожидать, в реализации операторов ++ для итератора.

Да и вообще, рассчитывать на оптимизатор конкретного компилятора - дело весьма рискованное. Качество оптимизации меняется от компилятора к компилятору (да и от версии к версии одного и того же компилятора), и воспринимать ее надо как дополнительный бесплатный бонус, а не как надежный аргумент к выбору той или иной конструкции языка. Тем более в данном случае для избежания зависимости от оптимизатора достаточно переставить два символа из конца идентификатора в начало; смешно, неправда ли? Да и вообще, IMHO голова гораздо более мощный оптимизатор, чем имеется у компилятора. Так зачем же ее отключать в угоду весьма сомнительным стилистическим удобствам?
Спасибо сказали: