Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Перегрузка =, ->, (), []


Автор: 0x07L 15.6.2006, 19:24
сабж 

Автор: skyboy 15.6.2006, 19:28
а функциями разве нельзя? или как надо? 

Автор: Earnest 15.6.2006, 20:03
Наверное, имеется в виду, почему нельзя перегрузить глобальными функциями...

Добавлено @ 20:12 
Пыталась представить себе глобальную перегрузку оператора, скажем ()... 

Стандартный оператор () применим только к функциям. Значит, перегрузим мы его глобально, и вместо вызова ожидаемой функции с некоторыми типами аргументов будет вызываться... что? Другая функция? Очень полезно. Главное, наглядно.

Аналогично с другими операциями: [] в стандартном виде применим только к указателям-массивам и только с целыми индексами. Перегрузили. С индексами типа complex. А что, нельзя? Какой смысл будет иметь выражение pA[c], где pA указатель, а с - complex? Ммм... наверное индексация в двумерном массиве, не иначе...

Ну и так далее. Тихо приходим к выводу, что смысл этих операций должен зависеть от типа левого операнда. А раз так, то имеет смысл выразить сию идею явно: запретить глобальную перегрузку. 

Автор: MAKCim 15.6.2006, 20:59
потому как это может нарушить семантику использования языковых средств
честно говоря даже не представляю, зачем это вообще надо (кроме разве что operator=) 

Автор: 0x07L 15.6.2006, 21:31
Зачем запрещать, если можно разрешить. Неужели найдутся программисты, которые захотят использовать индексы типа complex? А дополнительные возможности (даже такие сомнительные) тоже могут кому-нибудь пригодиться IMHO. 

Автор: DeadSoul 15.6.2006, 21:46
Цитата(0x07L @  15.6.2006,  21:31 Найти цитируемый пост)
Неужели найдутся программисты, которые захотят использовать индексы типа complex? 

std::map. 

Автор: MAKCim 15.6.2006, 21:49
Цитата

Зачем запрещать, если можно разрешить. Неужели найдутся программисты, которые захотят использовать индексы типа complex? А дополнительные возможности (даже такие сомнительные) тоже могут кому-нибудь пригодиться IMHO. 

если такой логике следовать, то в язык можно много чего еще ввести
стандартные операции в языке, имхо, не должны переопределяться, для этого вводится новый пользовательский тип и уже с ним можно извращаться  smile 

p.s приведи хоть один пример, где перегрузка глобальных [], (), -> может пригодится? 

Автор: 0x07L 15.6.2006, 22:09
Цитата(MAKCim @  15.6.2006,  21:49 Найти цитируемый пост)
если такой логике следовать, то в язык можно много чего еще ввести

Я не думаю, что разрешить перегрузку этих операторов - столь сложная задача.

Цитата(MAKCim @  15.6.2006,  21:49 Найти цитируемый пост)
приведи хоть один пример, где перегрузка глобальных [], (), -> может пригодится? 

До примеров я сейчас не додумаюсь. И прежде всего мне надо убедиться (а завтра убедить препода, который, возможно, задаст мне этот вопрос на зачете), что таких примеров нет.

PS Приведите, пожалуйста, пример, когда дружественной функцией перегружается оператор , (запятая). Вроде, ничуть не лучше, чем [ ]. Мне чего-то это начинает казаться бессмысленным.

Цитата(DeadSoul @  15.6.2006,  21:46 Найти цитируемый пост)
Цитата(0x07L @  15.6.2006,  21:31 )    
Неужели найдутся программисты, которые захотят использовать индексы типа complex?  

std::map. 

Это другое дело smile

Цитата(Earnest @  15.6.2006,  20:03 Найти цитируемый пост)
 Тихо приходим к выводу, что смысл этих операций должен зависеть от типа левого операнда. А раз так, то имеет смысл выразить сию идею явно: запретить глобальную перегрузку. 

А если левый операнд - член какого-нибудь библиотечного класса, к которому мы не можем добавить метод. Тогда, по аналогии с cin/cout мы можем только использовать дружественную функцию.

То есть нам, к примеру, нужно реализовать оператор вида a[ b ], где а - объект библиотечного класса, b - объект класса, который проетируем мы. Мы не можем добавить operator[] к методам библиотечного класса. Мы не имеем возможности реализовать такое методом нашего класса, поскольку ему через указатель this передается левый операнд. Остается только добавить к нашему классу дружественную функцию operator[] (const A & a, const B & b) или что-нибудь подобное.

Цитата(MAKCim @  15.6.2006,  20:59 Найти цитируемый пост)
кроме разве что operator= 

А чем operator= лучше operator[] или ...-> или ...()? 
   

Автор: skyboy 15.6.2006, 22:35
Цитата(0x07L @  15.6.2006,  22:09 Найти цитируемый пост)
А чем operator= лучше operator[] или ...-> или ...()? 

я бы добавил ещё сравнение ==
Почему только это? А потому что копирование объектов ("=") может сопровождаться кучей условий и вызовом конструкторов, который не совершает "стандартная" операция копирования объектов. Почему сравнение? А чтоб упростить сравнение сложных типов - тех же списков или структур. 

Автор: Earnest 16.6.2006, 07:33
Цитата(0x07L @  15.6.2006,  23:09 Найти цитируемый пост)
А если левый операнд - член какого-нибудь библиотечного класса, к которому мы не можем добавить метод. 

Есть такое понятие - адаптер. Пиши свой класс, и делай с ним, что хочешь.

Если каждый раз, когда нам потребуется добавить какому-то классу функциональность, мы будем изменять его интерфейс, мы очень скоро придем к куче ... ммм ... Это самый тупой способ сопровождения кода. И самый опасный. Под изменением интерфейса я понимаю не только функции члены, но и любой интерфейс класса в расширенном понимании. Если же переопределять глобальный оператор не в интерфейсе класса (рядом с классом), а где-то еще - получим все прелести разного поведения в зависимости от подключенных заголовков.

Цитата(skyboy @  15.6.2006,  23:35 Найти цитируемый пост)
я бы добавил ещё сравнение ==

Как раз этот оператор можно переопределить глобально. Что, кстати, не очень здорово: можно запросто получить программу, где в одном месте объекты A сравниваются одним способом, а в другом - совершенно другим.

 

Автор: skyboy 16.6.2006, 10:01
Earnest, знач, переопределять только в front-end модуле,  только в котором мы это сравнение и будем использовать и который не будет подключаться к другим модулям(в смысле, по задумке ЕГО не будут подключать) 

Автор: Earnest 16.6.2006, 10:23
Это из серии "благими намерениями". 

Исходный код имеет тенденцию жить своей жизнью. Сначала ты переопределяешь глобальный оператор (скажем сравнения объектов A) в cpp-файле, и счастлив - быстро, удобно, работает. Потом постепенно разрастается  до неприличных размеров, и надо его делить. А как же наш специальный оператор? Он уже должен использоваться в 2 файлах. Дублируем код? Выносим в заголовок? И то, и другое одинаково плохо. 
Причем, заметь, это может сделать совсем другой программист (а не автор первой версии кода). Или автор, но через несколько лет.

Если какая-то гадость может случится, она рано или поздно обязательно случится. Причем тогда, когда об этой милой особенности уже никто не помнит.

А искать такие ошибки - сплошная радость. Гораздо проще написать класс - обертку, подогнав под свои нужды. Это уж точно никогда никому не повредит. 

Автор: MAKCim 16.6.2006, 10:39
Цитата

Приведите, пожалуйста, пример, когда дружественной функцией перегружается оператор , (запятая). Вроде, ничуть не лучше, чем [ ]. Мне чего-то это начинает казаться бессмысленным.

OK
Код

#include <vector>
#include <iostream>

class container
{
private:
    std::vector<int> array_;
private:
    class reader
    {
    private:
        container& reference_;
    public:
        reader(container& ref, int obj)
            : reference_(ref) { reference_.array_.push_back(obj); }

        friend reader operator,(reader rd, int obj)
        {
            return reader(rd.reference_,obj);
        }
    };

    friend class reader;
public:
    reader operator<<(int obj)
    {
        return reader(*this,obj);
    }

    void print(std::ostream& stream)
    {
        for (std::vector<int>::iterator i=array_.begin();
                i!=array_.end(); ++i) stream<<*i;
    }
};

int main()
{
    container p;
    p<<1,2,3,4;
    p.print(std::cout);
    return 0;
}
 

Автор: skyboy 16.6.2006, 10:54
Earnest, полностью согласен. И рад, что в жизни не переопределял операторы, ибо с С++ почти не работаю  smile  

Автор: 0x07L 16.6.2006, 15:16
2_MAKCim

Просто здорово. Не знал, что перегрузка запятой может быть так полезна.
Хотя, если я не ошибаюсь, код можно было написать и без перегрузки запятой,
пользуясь лишь "<<" (это из области "кому что больше нравится")

2_Earnest

Дельное замечание.

---------------------------

Сегодня меня чуть не побили за то, что я задумал перегружать дружественными функциями операторы (), [], ->.
Приводили мне те же аргументы, что и Earnest в своем первом посте (левый операнд важнее правого).
Таким образом, это уже не личное мнение, а позиция большинства.
Так что от этих операндов я, пожалуй, отстану.

А вот относительно оператора присваивания меня переубедить не смогли.
Хотя...

Как известно, operator=, если не переопределен, конструируется (если можно так выразиться) компилятором и осуществляет побайтовое копирование объекта. Переопределяют operator= прежде всего для того, чтобы обеспечить правильную работу указателей-членов класса.  А тот, кто переопределяет operator= стандартного класса дружественной функцией для поддержки возможности присваивания объекту такого класса объекта нашего класса, не имеет доступа к этим указателям. Между тем смысл оператора присваивания в том, чтобы полностью менять левый операнд (а не прибавлять к какому-нибудь элементу объекта какое-либо значение, не делить, не умножать, не возводить в степень, ... не получать доступ к какому-либо элементу), что предполагает изменение этих указателей. Получается, вообще говоря, хрень и никому эта хрень, естественно, не нужна. Вопрос по operator= решен.

Если моя версия не вызывает ни у кого сомнений, пожалуй, стоит пометить вопрос как решенный. 

Автор: UnrealMan 16.6.2006, 16:38
Цитата(Earnest @  16.6.2006,  10:23 Найти цитируемый пост)
Выносим в заголовок? И то, и другое одинаково плохо.

Чем плох вынос в заголовок такого глобального определения:

Код
template <class T1, class T2>
bool operator !=(T1 &t1, T2 &t2)
{
    return !(t1==t2);
}

Автор: Earnest 16.6.2006, 17:39
Уточняю: плохо не вообще выносить такие определения в заголовочные файлы, а отделать их от интерфейса класса. Другими словами, если A - библиотечный класс (в файле, который ты не должен менять), а ты определяешь для него какой-нибудь глобальный оператор,  для определенности ==, в другом заголовочном файле, ты создаешь возможность разного сравнения объектов A в разных модулях. Т.е. все начинает зависеть от того, включена ли твоя версия оператора или нет. Если учесть, что h-файлы имеют тенденцию вкладываться друг в друга, то в один прекрасный момент может случиться так, что добавление еще одного include с какой-нибудь нужной функциональностью, приведет к изменению сравнения объектов A. Причем сразу это можно и не заметить.

Избежать эту ситуацию можно только если последовательно придерживаться принципа:
весь интерфейс объекта должен быть определен там же, где и сам объект. Нужно тебе локально изменить сравнение объектов A - заверни их в класс B и реализуй новые операторы для него.
Это относится ко всем общеупотребимым операторам: +, <, etc.

 

Автор: UnrealMan 16.6.2006, 18:13
Цитата
Чем плох вынос в заголовок такого глобального определения

Уже вижу, чем :-) С переопределением оператора != под конкретный класс (при необходимости) возникают проблемы (операторы-друзья вообще идут лесом, независимо от константности ссылок T&). Хотя в чём может заключаться эта необходимость?

Цитата(Earnest @  16.6.2006,  17:39 Найти цитируемый пост)
в один прекрасный момент может случиться так, что добавление еще одного include с какой-нибудь нужной функциональностью

Применительно к моему примеру (!= и == можно поменять местами) функциональность вряд ли будет изменена. 

Автор: DeadSoul 16.6.2006, 20:32
UnrealMan, тем, что ты научишься сравниваит и мои классы тоже. 

Автор: MAKCim 16.6.2006, 21:00
Цитата

код можно было написать и без перегрузки запятой,
пользуясь лишь "<<" 

так неинтересно  smile  

Автор: UnrealMan 17.6.2006, 10:45
Цитата(DeadSoul @  16.6.2006,  20:32 Найти цитируемый пост)
UnrealMan, тем, что ты научишься сравниваит и мои классы тоже. 

Не понял. Попробуй объяснить попонятней. 

Автор: DeadSoul 17.6.2006, 11:31
Цитата(UnrealMan @  17.6.2006,  10:45 Найти цитируемый пост)
Цитата(DeadSoul @  16.6.2006,  20:32 )
UnrealMan, тем, что ты научишься сравниваит и мои классы тоже. 
Не понял. Попробуй объяснить попонятней.  

После включения h-файла c твоим шаблоном operator!= будет определен для ВСЕХ классов содержащих operator!=. А ты не думаешь, что я специально не создавал оператор!=?!
 

Автор: UnrealMan 17.6.2006, 12:36
Цитата(DeadSoul @  17.6.2006,  11:31 Найти цитируемый пост)
А ты не думаешь, что я специально не создавал оператор!=?!

Это ещё зачем специально его не создавать?

Добавлено @ 12:37 
Цитата(DeadSoul @  17.6.2006,  11:31 Найти цитируемый пост)
будет определен для ВСЕХ классов содержащих operator!=. 

Ты, должно быть, хотел сказать "содержащих operator=="? 

Автор: DeadSoul 17.6.2006, 13:13
Цитата(UnrealMan @  17.6.2006,  12:36 Найти цитируемый пост)
Цитата(DeadSoul @  17.6.2006,  11:31 )
А ты не думаешь, что я специально не создавал оператор!=?!
Это ещё зачем специально его не создавать?

Затем же зачем конструкторы делают explicit. Чтобы компилятор сообщал мне об ошибках



Цитата(UnrealMan @  17.6.2006,  12:36 Найти цитируемый пост)
Цитата(DeadSoul @  17.6.2006,  11:31 )
будет определен для ВСЕХ классов содержащих operator!=. 
Ты, должно быть, хотел сказать "содержащих operator=="?  

Ну да 

Автор: UnrealMan 17.6.2006, 13:22
Т.е. оператор== создали, а != запретили. Не пойму, в чём прикол. Смысл такого запрета? Операторы не запрещают для того, чтобы компилятор выдавал ошибку (ошибка – это следствие запрета, а не причина :-) ). 

Автор: DeadSoul 17.6.2006, 14:05
Цитата(UnrealMan @  17.6.2006,  13:22 Найти цитируемый пост)
Т.е. оператор== создали, а != запретили. Не пойму, в чём прикол. Смысл такого запрета? 

В том, что мне НЕ нужен оператор != 

Автор: LPBOY 17.6.2006, 14:25
Цитата(0x07L @  15.6.2006,  19:24 Найти цитируемый пост)
Почему только методами?  

Где-то слышал (по крайней мере про operator= ), что запретили его глобальный аналог, потому что в этом случае возможны любые преобразования над объектом по правую сторону от него, что не очень интуитивно. 

Автор: UnrealMan 17.6.2006, 15:16
Цитата(DeadSoul @  17.6.2006,  14:05 Найти цитируемый пост)
В том, что мне НЕ нужен оператор !=  

Ну, не используй, коли не нужен :-) Запрещать-то зачем? 

Автор: DeadSoul 18.6.2006, 16:57
Цитата(UnrealMan @  17.6.2006,  15:16 Найти цитируемый пост)
Цитата(DeadSoul @  17.6.2006,  14:05 )
В том, что мне НЕ нужен оператор !=  
Ну, не используй, коли не нужен :-) Запрещать-то зачем?  

Зачем, что бы при попытке использования данного оператора(который я не писал) я получил ошибку компилятора, а не непонятные глюки
 

Автор: UnrealMan 18.6.2006, 18:18
Цитата(DeadSoul @  18.6.2006,  16:57 Найти цитируемый пост)
что бы при попытке использования данного оператора(который я не писал) я получил ошибку компилятора

Зачем тебе эта ошибка? Раз ты пытаешься использовать этот оператор, значит, он тебе уже нужен. Причём нужен именно в значении отрицания == (в противном случае это какой-то очень странный стиль программирования получается).

Цитата(DeadSoul @  18.6.2006,  16:57 Найти цитируемый пост)
а не непонятные глюки

Какие здесь могут быть глюки? 

Автор: DeadSoul 18.6.2006, 18:47
Цитата(UnrealMan @  18.6.2006,  18:18 Найти цитируемый пост)
Цитата(DeadSoul @  18.6.2006,  16:57 )
что бы при попытке использования данного оператора(который я не писал) я получил ошибку компилятора
Зачем тебе эта ошибка? Раз ты пытаешься использовать этот оператор, значит, он тебе уже нужен.

Код

class A;
void Foo(A a)// тут должна быть передача по ссылке, а не копирование. 
{
}

ВОТ ЗАЧЕМ
 

Автор: UnrealMan 18.6.2006, 20:52
Цитата(DeadSoul @  18.6.2006,  18:47 Найти цитируемый пост)
void Foo(A a)// тут должна быть передача по ссылке, а не копирование

Это ещё с какой радости? Хочешь (гарантированно) передачу по ссылке – изволь писать
A &a или
const A &a (если a не модифицируется и нужно оставить возможность неявных преобразований). 

Автор: DeadSoul 18.6.2006, 21:23
Цитата(UnrealMan @  18.6.2006,  20:52 Найти цитируемый пост)
Это ещё с какой радости? Хочешь (гарантированно) передачу по ссылке – изволь писать
A &a или

Я забыл\опечатался и т.д. 

Автор: 0x07L 19.6.2006, 18:08
Цитата(LPBOY @  17.6.2006,  14:25 Найти цитируемый пост)
Где-то слышал (по крайней мере про operator= ), что запретили его глобальный аналог, потому что в этом случае возможны любые преобразования над объектом по правую сторону от него, что не очень интуитивно. 

Не, не катит. Почему же тогда не запретили operator+=? Там преобразования правого аргумента, по-моему, не более интуитивны.
 

Автор: UnrealMan 19.6.2006, 22:36
‎ 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)