2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему
 
 Возврат значения классового типа в С++
Сообщение29.12.2019, 14:30 


06/04/18

323
Имеется конкретный код:

код: [ скачать ] [ спрятать ]
Используется синтаксис C++
class X {
  int i;
public:
  X(int ii = 0);
};

X::X(int ii) { i = ii; }

X func() {
  return X();
}

int main()
{  
    func() = X(1);
    return 0;
}
 
Вот что я понял: определяется некий конструктор по умолчанию с параметром, обладающим дефолтным значением 0. Дальше есть функция func, которая по идее должна возвращать значение классового типа. Что тогда в строке 10 инструкция return X(); на самом деле возвращает? Вызов конструктора? Для какого объекта? Но ведь конструктор сам в свою очередь никаких значений не возвращает. Я не понимаю этого синтаксиса и ещё больше не понимаю, что делает в строке 15 вызов функции в левой части присваивания. Нужна помощь зала.

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение29.12.2019, 15:29 
Заслуженный участник


27/04/09
28128
Qlin в сообщении #1432538 писал(а):
Но ведь конструктор сам в свою очередь никаких значений не возвращает.
Он пишется так, что в его теле не нужно и нельзя писать возврат чего-то явным образом, но вообще, «извне», это функция, возвращающая значение соответствующего класса. Так что func() делает ровным счётом то же что X().

Когда вы видите конструкцию навроде C x(...); — синтаксически это не вызов конструктора, это вид инициализации, и её можно делать для вещей вовсе без конструкторов, типа int n(2); (проверка). Она понимается примерно как C x; x = C(...);, хотя скорее всего в стандарте всё хитрее.

А про присваивание я уже ничего не скажу, ерунда скорее всего получится. По идее временная переменная инициализируется результатом func(), а потом ей присваивается X(1), но я удивлён, что func() — это lvalue.

-- Вс дек 29, 2019 17:30:45 --

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

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение29.12.2019, 15:42 
Заслуженный участник
Аватара пользователя


06/10/08
6422
arseniiv в сообщении #1432541 писал(а):
А про присваивание я уже ничего не скажу, ерунда скорее всего получится. По идее временная переменная инициализируется результатом func(), а потом ей присваивается X(1), но я удивлён, что func() — это lvalue.
Оно не lvalue, просто здесь func() = X(1); это же на самом деле func().operator=(X(1));, и оператору никто не мешает вызываться от rvalue (в стандарте есть специальное исключение по поводу того, что неявный аргумент, предтавляющий вызываемый объект, может связываться с rvalue).

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение29.12.2019, 16:01 


10/04/12
705
Я вижу так:

1. Вызывается функция func()
2. В ней создаёться объект O1 как X()
3. Выходим из функции. Создаётся временный объект O2 который инициализуется конструктором копирования из O1. Он не определён, поэтому просто все поля копируются. O1 уничтожается.
4. Потом создаёться объект O3 как X(1)
5. Затем вызывается операция присваивания = объекту O2 значения O3. Опять же, она не определена, поэтому простое копирование.
6. А потом временные объекты O2 и O3 просто уничтожаются, потому что мы выходим за скоуп оператора.

Да, код не имеет эффекта, как и 2+2;, но тут компилятору это сложнее обнаружить.

В целом оптимизатор скорее всего имеет полномочия отождествить O1 и O2 и выпилить создание/удаление одного объекта (считается что конструктор копирования не должен иметь сайд-эффектов).

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение29.12.2019, 16:18 
Заслуженный участник
Аватара пользователя


06/10/08
6422
Да, так оно и есть. Хотя, конечно, давать такой код людям не до конда разобравшимся с вызовом конструкторов - это извращение.

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение30.12.2019, 04:13 


06/04/18

323
arseniiv в сообщении #1432541 писал(а):
вы видите конструкцию навроде C x(...);
В данном случае я вижу синтаксис навроде C (...) :wink:
arseniiv в сообщении #1432541 писал(а):
это вид инициализации, и её можно делать для вещей вовсе без конструкторов, типа int n(2)
Если соблюдать аналогию точно, то должно быть не int n(2), а int (2).
mustitz в сообщении #1432548 писал(а):
В ней создаёться объект O1 как X()
Я попробовал внедрить в код эту инструкцию: cout << &X(); Компилятор выдает ошибку:error: taking address of temporary [-fpermissive]. В то же время допускаются выкрутасы вроде X()=X(1);.

Ситуация для меня непонятная: X() создаёт временный объект, у которого есть какое-то время жизни (интересно, какое ?), но при этом он может оказаться в любой части присваивания. Так же и вызов функции, который возвращает этот временный объект (или его копию ?), может оказаться в любой части присваивания.
Xaositect в сообщении #1432545 писал(а):
func() = X(1); это же на самом деле func().operator=(X(1));
Я не знаю, что делает ключевое слово operator. Гуглил, не помогло.

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение30.12.2019, 06:24 
Заслуженный участник


16/02/13
4214
Владивосток
arseniiv в сообщении #1432541 писал(а):
её можно делать для вещей вовсе без конструкторов, типа int n(2);
Не бывает вещей без конструкторов. Другое дело, что некие простейшие конструкторы ++ дописывает за вас.

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение30.12.2019, 11:03 


10/04/12
705
Qlin в сообщении #1432624 писал(а):
mustitz в сообщении #1432548 писал(а):
В ней создаёться объект O1 как X()
Я попробовал внедрить в код эту инструкцию: cout << &X(); Компилятор выдает ошибку:error: taking address of temporary [-fpermissive]. В то же время допускаются выкрутасы вроде X()=X(1);.


Оператор = можно перегрузить, и его семантика в принципе может отличаться от копирования. Вообще, за копирование отвечает конструктор копирования. А присваивание это просто операция, для которой есть дефолтная реализация. Для каких-либо умных указателей там может быть увеличение счётчика ссылок, и т. д. и т. п. Поэтому компилятор не может строить никаких предположений, зачем он вызван. Короче говоря, можно извратиться и сделать так, что код X() = X(1); действительно будет иметь какой-то эффект. Например, в присваивании мы устанавливает флажок копии, в деструкторе выводим на cout значение i. И тогда этот код будет а-ля распечатай значение 1.

Ну а взятие адреса от временного объекта это ошибка, потому что не гарантируется время его жизни, ни вообще его существование (скажем выпилил оптимизатор). Но, я думаю, если перегрузить взятие адреса
X* operator&() const { return this; }, то пожалуйста, стреляй себе в ногу как хочешь :)

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение30.12.2019, 12:59 
Заслуженный участник


16/02/13
4214
Владивосток
Qlin в сообщении #1432624 писал(а):
какое-то время жизни (интересно, какое ?)
Ну уж это-то время определено в стандарте. Скорее всего, до конца объемлющего блока.

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение30.12.2019, 13:46 


10/04/12
705
iifat в сообщении #1432663 писал(а):
Ну уж это-то время определено в стандарте. Скорее всего, до конца объемлющего блока.


Это определено для объектов, которые описывает пользователь. А вот временные объекты, которые создаёт сам компилятор. Думаю там многое отдано на откуп оптимизатору. Поэтому и нельзя брать адрес от временного объекта. Ну и предел жизни временного объекта — оператор, а не блок.

 Профиль  
                  
 
 Re: Возврат значения классового типа в С++
Сообщение30.12.2019, 14:47 
Заслуженный участник


27/04/09
28128
Qlin в сообщении #1432624 писал(а):
В данном случае я вижу синтаксис навроде C (...) :wink:
Так я имел в виду не здесь. Вы же упоминали, что
    Qlin в сообщении #1432538 писал(а):
    Вызов конструктора? Для какого объекта? Но ведь конструктор сам в свою очередь никаких значений не возвращает. Я не понимаю этого синтаксиса
и я потому решил, что вы знакомы с синтаксисом инициализации и сравниваете этот с ним.

iifat в сообщении #1432630 писал(а):
Не бывает вещей без конструкторов.
CppReference вроде не пишет о конструкторах для типов, не определяемых пользователем:
Конструкция int(...), когда это не каст — не вызов «конструктора» int, а инициализатор.

-- Пн дек 30, 2019 16:54:04 --

Qlin в сообщении #1432624 писал(а):
Я не знаю, что делает ключевое слово operator. Гуглил, не помогло.
https://en.cppreference.com/w/cpp/language/operators.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 11 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group