Здравствуйте, Максим Рогожин, Вы писали:
МР>Почему нельзя писать так
МР>class A { МР>public: МР> void A() {} МР>}; МР>?
Страуструп писал, что это для подчёркивания того факта, что конструктор никогда не зовётся напрямую.
МР>Отличается ли из-за этого низкоуровневая организация вызова этой функции (организация стека, пролог, эпилог)?
В этом случае тот конструктор, что с C3, аллоцирует память и возвращает адрес объекта, а те, что C1 и C2, получают первым скрытым параметром адрес объекта и ничего не возвращают.
Больше отличий нет, остальное идентично обычной функции.
Это понятно. Но я думал, что void как раз и означает что функция не возвращает СОВСЕМ НИКАКОГО значения. Это не так? Раз это не так, то отличается ли организация вызова конструктора от вызова обычных функций, например таких:
int f(int x) { return x*x; }
void g() { return; }
Я имею ввиду является ли трансляция C++ вызова конструктора в ассемблерный код каким-то специальным частным случаем, который обрабатывается способом отличным от способа, которым транслируются вышеприведенные функции int f(int) и void g(), например?
Здравствуйте, Максим Рогожин, Вы писали:
МР>Это понятно. Но я думал, что void как раз и означает что функция не возвращает СОВСЕМ НИКАКОГО значения. Это не так? Раз это не так, то отличается ли организация вызова конструктора от вызова обычных функций, например таких: МР>
МР>Я имею ввиду является ли трансляция C++ вызова конструктора в ассемблерный код каким-то специальным частным случаем, который обрабатывается способом отличным от способа, которым транслируются вышеприведенные функции int f(int) и void g(), например?
Говорить об организации вызова и кодогенерации можно лишь в привязке к каким-то конкретным компиляторам и платформам. То есть это уже детали реализации, но никак не свойства самомго языка, описанные в стандарте. Исходя из самых общих соображений, вряд ли можно придумать какие-то причины, по которым кодогенерация конструктров может принципиально отличаться от кодогенерации функций с типом результата void. При этом синтаксис и семантика имеют известные существенные отличия.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Это понятно. Но я думал, что void как раз и означает что функция не возвращает СОВСЕМ НИКАКОГО значения. Это не так? Раз это не так, то отличается ли организация вызова конструктора от вызова обычных функций, например таких:
Это просто синтаксис. Ну вот хотел создатель зачем-то подчеркнуть, что конструктор это не функция и сделал чтобы он не мог участвовать с синтаксических конструкциях с void.
МР>Я имею ввиду является ли трансляция C++ вызова конструктора в ассемблерный код каким-то специальным частным случаем, который обрабатывается способом отличным от способа, которым транслируются вышеприведенные функции int f(int) и void g(), например?
Может является, а может и нет. Вы можете посмотреть ассемблерный листинг, если интересно.
МР>Глядя на эту запись можно подумать, что здесь конструктор именно зовется напрямую и возвращает значение, аналогично записи МР>
МР>A obj = createObject(1024);
МР>
Эти две записи похожи лишь формально. Только в первом случае это является не возвращением значения, а прямым конструированием объекта в правой части инициализатора. Это то же самое выражение, которое используется в операторе return в функции createObject:
A createObject(int value)
{
return A(value);
}
--
Справедливость выше закона. А человечность выше справедливости.
МР>Я имею ввиду является ли трансляция C++ вызова конструктора в ассемблерный код каким-то специальным частным случаем, который обрабатывается способом отличным от способа, которым транслируются вышеприведенные функции int f(int) и void g(), например?
Функция она и в Африке функция, даже если она — конструктор. Отличаются они только соглашениями вызова функций. Вот посмотри тут, например, calling conventions. Для конструктора нет особого типа функции, т.е. это примерно то, что ты думаешь. Однако, как тут уже указали, синтаксис и семантика конструктора отличаются от обычной функции, поэтому, реализация тоже может отличаться. В результате реализаций конструкторов несколько (опять же как уже написали) с выделением памяти, без него, другие варианты. Таким образом, в коде конструктора оказывается не только тот код, который написан в исходном файле.
P.S. Для виртуальных функций, кстати, тоже нет "особого" типа. Вполне себе обычные функции-члены, только у каждого класса есть еще таблица виртуальных функций (в стандарте реализация не прописана, но все компиляторы делают через таблицу виртуальных функций).
Здравствуйте, Максим Рогожин, Вы писали:
МР>Для чего запрещать конструктору возвращать даже void, но при этом разрешать следующий синтаксис?
МР>class A { МР>public: МР> A(int); МР>};
МР>A obj = A(1024);
А почему этот синтаксис надо запрещать? Вы тут создали безымянный объект типа A и затем скопировали его в obj. Это другое, чем просто написать A obj(1024);.
Компилятор, скорее всего, соптимизирует это в простую инициализацию (если ему не запретить созданием своих конструкторов копирования и перемещения, или введением в A нетривиальных полей), но это не меняет, что изначально это разные вещи.
Здравствуйте, netch80, Вы писали:
N>А почему этот синтаксис надо запрещать?
Потому, что конструктор это функция, которая не возвращает никаких значений. Вот, например
void createObject(int index) {
A(index);
}
A obj = createObject(1024); // так же нельзя написать.
A obj = A(1024); // значит и так нельзя писать... Потому, что функция-конструктор,
// с точки зрения синтаксиса, похожа на функцию createObject(int) тем,
// что у нее нет возвращаемого значения.
Здравствуйте, Максим Рогожин, Вы писали:
N>>А почему этот синтаксис надо запрещать? МР>Потому, что конструктор это функция, которая не возвращает никаких значений. Вот, например МР>
МР>A obj = A(1024); // значит и так нельзя писать... Потому, что функция-конструктор,
МР> // с точки зрения синтаксиса, похожа на функцию createObject(int) тем,
МР> // что у нее нет возвращаемого значения.
МР>
Ты сам себя загоняешь в заблуждение, пытаясь рассматривать это выражение как значение, возвращаемое функцией. В то время как никто никого здесь не возвращает — это прямое конструировние объекта имя типа которого задается конструктором. Это по смыслу то же самое что и
int obj = int(1024);
Нет здесь никакого возвращаемого значения, видишь?
Сам по себе конструктор — это всего лишь процедура, единственная задача которой сконструировать объкт в заданной области памяти. Указатель на эту область памяти передается конструктору неявно, и доступна эта область памяти внутри конструктора как this. Никаких значений он возвращать не может в принципе, потому что один и тот же конструктор может вызываться:
для создания rvalue объекта во временной области памяти:
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, Максим Рогожин, Вы писали:
N>>>А почему этот синтаксис надо запрещать? МР>>Потому, что конструктор это функция, которая не возвращает никаких значений. Вот, например МР>>
МР>>A obj = A(1024); // значит и так нельзя писать... Потому, что функция-конструктор,
МР>> // с точки зрения синтаксиса, похожа на функцию createObject(int) тем,
МР>> // что у нее нет возвращаемого значения.
МР>>
R>Ты сам себя загоняешь в заблуждение, пытаясь рассматривать это выражение как значение, возвращаемое функцией. В то время как никто никого здесь не возвращает — это прямое конструировние объекта имя типа которого задается конструктором.
Это выглядит как значение возвращенное функцией. А затем присвоенное с помощью оператора присвоения.
По идее, такое конструирование — это избыточность в языке, достаточно было оставить
Здравствуйте, rg45, Вы писали:
R>Говорить об организации вызова и кодогенерации можно лишь в привязке к каким-то конкретным компиляторам и платформам. То есть это уже детали реализации, но никак не свойства самомго языка, описанные в стандарте. Исходя из самых общих соображений, вряд ли можно придумать какие-то причины, по которым кодогенерация конструктров может принципиально отличаться от кодогенерации функций с типом результата void. При этом синтаксис и семантика имеют известные существенные отличия.
Есть конструкторы полей и баз, в том числе и виртуальных, соответственно, есть порядок вызова и обработка исключений во всех этих конструкторах, всего этого нет в обычной функции/методе.
Кроме того, соглашение о вызове у метода и функции обычно разные, у деструктора/конструктора они тоже могут быть своими, особенными...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, netch80, Вы писали:
N>Компилятор, скорее всего, соптимизирует это в простую инициализацию (если ему не запретить созданием своих конструкторов копирования и перемещения, или введением в A нетривиальных полей), но это не меняет, что изначально это разные вещи.
В этом месте компилятор всегда может пропускать конструктор копии, но обязан проверять его доступность...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, rg45, Вы писали:
R>И это все одна и та же процедура! Как она может что-то возвращать при таком разнообразии сценариев использования?
Ещё создание поля, базы и элемента массива забыл.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском