Кому-то может показаться прекрасной идеей перегрузить для своего контейнера оператор & для добавления, а оператор | для удаления элементов. Другой может воспользоваться для этого операторами + и -. Однако почти всегда предпочтительней использовать просто функции: push/pop, add/remove и т.п. Имена функций, в отличии от операторов, более наглядно отображают суть действия. И при обращении к объекту через указатель, его не требуется разыменовывать:
MyStack<int> *ptrStack = &stack;
// Допустим, класс MyVector предоставляет перегруженный
// оператор += для добавления нового элемента
stack += 10; // Пока все нормально
ptrStack += 10; // Ой! Это не добавление, а изменение указателя.
(*ptrStack) += 10; // Теперь правильно. Но выглядит ужасно!
// А теперь используя методы
stack.push( 10 ); // Все нормально
ptrStack->push( 10 ); // И тут все нормально
По этой же причине, при создании класса контейнера, т.е. с перегруженным оператором [ ], следует также реализовывать метод at.
MyVector<int> *vector = new MyVector<int>();
...
int a = vector[10]; // Ошибка, это не вызов перегруженного оператора.
int b = (*vector)[10]; // Теперь правильно, но также некрасиво.
int c = vector->at(10); // Так все-таки лучше.
Особенно неприятна перегрузка операторов && и ||. Все мы знаем и широко используем одно из свойств языка C++, которое гарантирует, что в случае сравнения, если левая часть выражения будет равна false, то вторая не будет вычислена (т.е. ленивое вычисление). И используем это примерно вот так:
const char *p = ...
...
if( p && strlen(p) )
// что-то выполняем
Так вот, в случае, когда компилятор встречает перегруженную версию операторов && или || это больше не работает. Они полностью меняют свое поведение. После перегрузки это будет как вызов метода, все аргументы которого вычисляются. И при этом в произвольном порядке.
Не менее неприятное поведение произойдет при перегрузке оператора , (запятая). Стандартное его поведение гарантирует, что выражения, разделенные оператором запятая, будут сделаны слева направо:
int a = func2(), func1();
До перегрузки стандарт гарантирует следующую последовательность вызовов: func1() –> func2(). После перегрузки оператор должен быть вызван как отдельная функция или метод класса, в качестве аргументов которому будут переданы результаты вызовов funcX(). А порядок вычисления аргументов не определен.