Жёсткая задачка про ООП в С++

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by pch, 14 Jun 2007.

  1. pch

    pch Elder - Старейшина

    Joined:
    22 Oct 2005
    Messages:
    34
    Likes Received:
    41
    Reputations:
    15
    Прелюдия (можно смело пропустить)
    Вообщем хочется написать класс полиномов над некоторым типом aType так, чтобы операции были понолстью френд-перегружены, т.е. чтобы работало int==Polynom<int>, int*Polynom<int> и другие приятности.
    В не-шаблонном случае это - тривиальнейшая задача. Как только я ввожу шаблоны, aType престаёт приводиться к Polynom, хотя конструктор Polynon<int> имеется. Если выкинуть контекст, и оставить только саму проблему, то получится

    Задача
    Заставить работать парой волшебных пассов
    Code:
    #include <iostream>
    using namespace std;
    
    template <class aType>
    class testClass{
    public:
           aType data;
           testClass(aType c): data(c) {}
           bool friend operator==<aType>(testClass<aType>, testClass<aType>);
    };
    template <class aType>
    bool operator==(testClass<aType> a, testClass<aType> b){
           return a.data==b.data;
    }
    int main(){
           int a=4;
           testClass<int> b(4);
           if (a==b) cout << "a equals b\n";
           if (b==a) cout << "b equals a\n";
    }
    
    В таком состоянии выдает две ошибки:
    Code:
    no match for `testClass<int> & == int &`
    no match for `int & == testClass<int> &`
    Если вызывать явные конструкторы Polynom(a)==b, всё работает, но тогда инкапсуляция и всё изящество идёт лесом.

    Действительно хоть какое-то решение - делать приведения явно, но внутри класса. Т.е. добавив
    Code:
          
    bool friend operator==<aType>(aType a, testClass b<aType>);
    bool friend operator==<aType>(testClass a<aType>, aType b);
    
    Ни в одной книге попавшейся мне, включая Строуструпа, этого нет. Но кажется совсем сомнительным, что это невозможно, это ж С++!!! Вообщем, надеюсь прояснить этот вопрос
     
  2. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,118
    Reputations:
    1,139
    Ну ясен пень, ты сравниваешь целое число и объект класса. У тебя же нет оператора их сравнения. Кстати, что за компилер? Обычно такое компилеры кушают, создавая временные объекты.. хотя хз, я точно не знаю
     
  3. Aag

    Aag Elder - Старейшина

    Joined:
    26 Jul 2005
    Messages:
    60
    Likes Received:
    19
    Reputations:
    8
    По-моему, просто надо реализовать соответствующий оператор копирования, поскольку по умолчанию генерируется такой оператор копирования
    Code:
    testClass<aType>& testClass<aType>::operator = (const testClass<aType>& hs)
    
    вам для неявного преобразования надо реализовать такой оператор класса, который в качестве аргумента принимает не testClass<aType>& , а аргумент aType &
    Code:
    testClass<aType>& testClass<aType>::operator = (const aType& hs){
        data = hs;
        return *this;
    };
    
     
    #3 Aag, 14 Jun 2007
    Last edited: 14 Jun 2007
  4. pch

    pch Elder - Старейшина

    Joined:
    22 Oct 2005
    Messages:
    34
    Likes Received:
    41
    Reputations:
    15
    2 _Great_
    У меня есть функция сравнения двух объектов класса testClass<int> и есть котструктор testClass<int>(int). Так что компилятор может привети оба операнда к типу testClass<int> и сравнить. Более того, если выкинуть все шаблоны и везде заменить aType на int (а testClass<int> b(4) на testClass b(4)), то всё именно так и работает.
    Компялятор - gcc (win).

    2 Aag
    А где здесь копирование? Я же иницаализирую не элементом того же класса, а int-ом...
    В любом случае, добавление явного оператора копирования ничего не меняет, те же ошибки
     
    1 person likes this.
  5. Aag

    Aag Elder - Старейшина

    Joined:
    26 Jul 2005
    Messages:
    60
    Likes Received:
    19
    Reputations:
    8
    Компильнул твой код из начала темы. Ни одной ошибки. Все работает без изменений.
    Получил
    Code:
    a equals b
    b equals a
    
    Копилятор gcc 3.3.5 (*nix)

    про оператор присваивания я не прав. здесь действительно инициализация идет.
     
  6. pch

    pch Elder - Старейшина

    Joined:
    22 Oct 2005
    Messages:
    34
    Likes Received:
    41
    Reputations:
    15
    Пробовал на:
    BSD: gcc version 3.4.2 [FreeBSD] 20040728
    Linux: gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)
    Win: gcc version 2.95.2 19991024 (release)
    Win: gcc version 3.4.2 (mingw-special)

    И вcе выдают более-менее одну и ту же ошибку.

    Это однако ппц какой-то.
    Можете привести что точно выводит это компилятор на gcc -v ? Мож там ключи какие хитрые подефолту?
     
    #6 pch, 15 Jun 2007
    Last edited: 15 Jun 2007
  7. Aag

    Aag Elder - Старейшина

    Joined:
    26 Jul 2005
    Messages:
    60
    Likes Received:
    19
    Reputations:
    8
    gcc -v: gcc version 3.3.5

    Попробывал на mingw 3.4.5 под win действительно не работает.

    Но при минимальных изменениях все работает под mingw 3.4.5
    Code:
    #include <iostream>
    
    template <typename aType>
    class testClass{
    public:
           aType data;
           testClass(aType c): data(c) {};
           bool friend operator==(testClass<aType> a, testClass<aType> b){return a.data==b.data;} ;
    };
    
    
    int main(){
           int a=4;
           testClass<int> b(4);
           if (a==b) std::cout << "a equals b\n";
           if (b==a) std::cout << "b equals a\n";
    };
    
    то есть если внести описания друга внутрь класса.

    Вот посмотри еще http://en.wikipedia.org/wiki/Barton-Nackman_trick
     
    #7 Aag, 15 Jun 2007
    Last edited: 15 Jun 2007
    1 person likes this.
  8. pch

    pch Elder - Старейшина

    Joined:
    22 Oct 2005
    Messages:
    34
    Likes Received:
    41
    Reputations:
    15
    Ух-ты. Я просто в Ахуе. Огромный респект!
     
  9. ZaCo

    ZaCo Banned

    Joined:
    20 Jun 2005
    Messages:
    737
    Likes Received:
    336
    Reputations:
    215
    :) а так по-моему много проще:
    operator int()
    {
    return data;
    }
     
  10. pch

    pch Elder - Старейшина

    Joined:
    22 Oct 2005
    Messages:
    34
    Likes Received:
    41
    Reputations:
    15
    2 ZaCo

    Ну тогда уж не operator int(), а operator aType(), но в любом случае это - не то ;) . Этот оператор преобразует оба операнда к типу aType (в данном случае - int), и сравнивает их как инты. А если в классе не одно поле (например, рац числа над типом int), то приведение к инту даст лажу (можно приводить в частном случае к флоату, но в общем шаблонном случае непонятно к чему приводить).

    Убедиться, что оператор сравнения в классе testClass lдействительно не вызывается просто, достаточно добавить туда cout:
    Code:
    #include <iostream>
    using namespace std;
    
    template <class aType>
    class testClass{
    public:
    	aType data;
    	testClass(aType c): data(c) {}
    	bool friend operator==<aType>(testClass<aType>, testClass<aType>);
    	operator aType(){
    		return data;
    	}
    };
    template <class aType>
    bool operator==(testClass<aType> a, testClass<aType> b){
    	cout << "Operator == exucuted\n";
    	return a.data==b.data;
    }
    int main(){
    	int a=4;
    	testClass<int> b(4);
    	if (a==b) cout << "a equals b\n";
    	if (b==a) cout << "b equals a\n";
    }
    
    Этот код не выведет фразу "Operator == exucuted" ни разу