Неоднозначная ситуация с функцией преобразования в классе

Тема в разделе "LANGS.C", создана пользователем Ronin_, 2 июн 2017.

  1. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    Всех приветствую!

    Возникает неоднозначная ситуация при использовании функции преобразования char *, как только её убираю из класса, все начинает работать. При этом что плюс перегружается для объекта отдельно!

    Безымянный.png

    Код (C++):
    1.  
    2. #include <iostream>
    3. #include <cstring>
    4. using namespace std;
    5.  
    6. class Num {
    7.     int n;
    8.  
    9.     public:
    10.         explicit Num(int i) {
    11.             n = i;
    12.         }
    13.      
    14.         Num operator+(Num obj);
    15.      
    16.         friend ostream &operator<<(ostream &stream, Num &obj);
    17.      
    18.         operator int() {
    19.             return n;
    20.         }
    21.      
    22.         operator char *() {
    23.             return "Hello wasm!";
    24.         }
    25. };
    26.  
    27. ostream &operator<<(ostream &stream, Num &obj) {
    28.     stream << obj.n;
    29.  
    30.     return stream;
    31. }
    32.  
    33. Num Num::operator+(Num obj) {
    34.     Num temp(this->n);
    35.     temp.n = (temp.n + obj.n);
    36.  
    37.     return temp;
    38. }
    39.  
    40. int main() {
    41.  
    42.     Num goo(2017), obj(-3);
    43.     cout << goo << endl;
    44.     cout << goo + obj << endl;
    45.     cout << goo + 5 << endl;
    46.  
    47.     cout << strcmp(goo, "string") << endl;
    48.  
    49.     return 0;
    50. }
     
    Последнее редактирование: 2 июн 2017
  2. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Потому что здесь надо определить ещё operator + для двух аргументов вне класса.
    Ох уж эти плюсы с их перегрузкой.
     
  3. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    Приветствую. Каких ещё аргументов вне класса?

    Код (C++):
    1.    
    2. Num goo(2017), obj(-3);
    3.     cout << goo << endl;
    4.     cout << goo + obj << endl;
    5.     cout << goo + 5 << endl;
    6.     cout << strcmp(goo, "string") << endl;
    7.  
    Тут объект складывается с объектом, для такой ситуации перегружен оператор плюс, также отменено неявное преобразование к типу класса с помощью спецификатора explicit. Далее вызывается функция преобразования для сложения с целочисленным литералом. Функция strcmp ожидает символьный массив, поэтому должно произойти преобразование с помощью функции преобразования к указателю типа char.
     
  4. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Ronin_,
    Код (C):
    1.  
    2. Num operator + (const Num &a, int b);
    3.  
     
  5. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    SadKo, Тогда теряется смысл преобразования с помощью функции преобразования. Руками перегрузить оператор для ситуации когда слева не объект, а целочисленный литерал не проблема. :)
     
  6. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    Оператор вывода перегружен НЕкорректно для класса Num. Параметром должен быть const Num&. Тогда всё работает. https://wandbox.org/permlink/ToTwQ0dmHA6NC4kr
     
  7. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    CurryHowardIsomorphism, Вы удалили свой предыдущий ответ, используйте пожалуйста корректирование, чтобы связь ответов не потерять.

    Ваш пример также не заработал, компилятор из студии 14-ой.

    Параметр типа Num кстати можно и без ссылки объявлять, та как возвращается поток где нужна ссылка из функции оператора, сам объект перенаправлять можно и без ссылки во встроенный поток, если это был бы входной поток, то да. Модификатор моё упущение, но проблема осталась!
     
    Последнее редактирование: 3 июн 2017
  8. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    Ronin_, главное, что неконстантная ссылка не может захватывать временные объекты. Нравится по значению, а не по константной ссылке --- передавай по значению.

    "Не заработал" следует подкреплять конкретными сообщениями об ошибках.
     
  9. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    Если он по ссылке передается, то он никак не может быть временным так как на него генерируется указатель. Модификатор const всего лишь гарантирует что объект не будет модифицирован. :)

    Ошибка идентична предыдущему скрину первого поста.

    PS. g++ из пакета TDM GCC тоже не компилирует, у меня есть мысль по части решения вопроса, но озвучу позже как проверю. :)
     
  10. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    Вот тут http://rextester.com/l/cpp_online_compiler_visual работает.
    _MSC_VER там равен 1900, т.е. это MSVC++ 14.0 (Visual Studio 2015)
     
  11. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    :facepalm:
    Ссылки не имеют никакого отношения к указателям. То, что они где-то реализованы через указатели — с точки зрения стандарта и семантики языка ничего ровным счётом не значит.
    Стоит прочитать http://en.cppreference.com/w/cpp/language/lifetime, http://en.cppreference.com/w/cpp/language/value_category и http://en.cppreference.com/w/cpp/language/reference_initialization, а то так каша в голове и останется.
     
    Последнее редактирование: 4 июн 2017
  12. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    Давайте только без школьных вбросов, я же не переходил на личности.

    Код (C++):
    1. #include <iostream>
    2. using namespace std;
    3.  
    4. double val = 2546.17;
    5.  
    6. double &f() {
    7.     return val;
    8. }
    9.  
    10. double *f2() {
    11.     return &val;
    12. }
    13.  
    14. int main() {
    15.  
    16.     cout << "Before: " << f() << endl;
    17.     f() = 1984.1633;
    18.     cout << "After: " << f() << endl;
    19.  
    20.     *f2() = 1024;
    21.  
    22.     cout << *f2() << endl;
    23.  
    24.     return 0;
    25. }
    Почитайте что такое call by value и call by reference. Когда аргумент передается по ссылке, тоесть передается его адрес и когда копируется аргумент в параметр.
    Посему он временным не может быть, потому что передается его адрес на который указывает аргумент. :)

    И да, const по факту там не нужен, так как переменная экземпляра private. :)
     
  13. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    Я предпочитаю читать стандарт или выжимки из него, вроде тех ссылок, что я привёл выше. Все эти интуитивные псевдорассуждения меня не интересуют. Что такое временный объект и как работают ссылки — определено в стандарте.

    Что должен демонстрировать код — я не понял.

    Не знаю, что значит "по факту" в твоём понимании. "по факту" неконстантные lvalue-ссылки не биндятся к временным объектам.

    Советую поставить 2017-ю студию и компилировать код с флагом /permissive-. Ну или хотя бы с флагом /Zc:referenceBinding.
     
  14. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    Это само собой, но ещё не стоит неглежировать чтением людей которые принимали этот стандарт. :)

    Время жизни объекта который создается с помощью копии конструктора, когда объект передается как call by value функции, определяется выходом из области видимости функции.
    Время жизни объекта который передается как call by reference определяется выходом из области видимости функции из которой он передается, но тогда не создается копия и не вызывается копия конструктора. Например если объект передавать из точки входа(main), то время жизни объекта будет определяться выходом из области видимости функции main(завершением программы).

    Ну и там ещё подводные камни насчет деструкторов, new and delete etc...

    То что можно сделать с помощью ссылок, можно сделать с помощью указателя. Ссылки это те же указатели только с некоторыми специфическими особенностями.
     
  15. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    Ох уж эта кривая студия с её "поддержкой" плюсов :)
     
  16. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
     
  17. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    Ronin_, и с какими же сообщениями g++ из пакета TDM GCC не компилирует?
     
  18. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
     
  19. Ronin_

    Ronin_ Active Member

    Публикаций:
    1
    Регистрация:
    24 дек 2016
    Сообщения:
    252
    Вечером напишу, проверю свою мысль, возможно узнаем в чем трабла. :)

    Если через указатели реализовано, то уже имеет. :)
     
  20. CurryHowardIsomorphism

    CurryHowardIsomorphism Member

    Публикаций:
    0
    Регистрация:
    13 май 2017
    Сообщения:
    97
    Окончательное решение вопроса с error C2593: 'operator +' is ambiguous для cout << goo + 5 << endl;
    1. note: could be 'Num Num::operator +(Num)' — ну это, конечно, враньё, т. к. Num::operator+(Num) не участвует в overload resolution, т. к. конструктор Num::Num(int) отмечен как explicit.
    2. note: or 'built-in C++ operator+(int, int)'
      note: or 'built-in C++ operator+(const char *, int)'

      с этим сложнее, т.к. зависит от платформы. Стандарт определяет (как минимум) 2 built-in operator+:
      Всё дело в том, как определён ptrdiff_t.
      - Если это 32-битная студия, то ptrdiff_t это int и возникает неоднозначность. Можно либо сконвертировать Num в int и вызвать operator+(int, int), либо сконвертировать Num в const char* и вызвать operator+(const char*, int == std::ptrdiff_t). Эти два способа равноценны.
      - Если это 64-битная студия, то ptrdiff_t != int и для вызова operator+(const char*, std::ptrdiff_t) нужно сделать 2 преобразования: Num в const char* и int в std::ptrdiff_t. Для вызова же operator+(int, int) нужно только преобразовать Num в int. Выбирается перегрузка, требующая меньше преобразований. Неоднозначности нет.
     
    Последнее редактирование: 7 июн 2017