Re[8]: Почему нельзя писать void ctor();
От: rg45 СССР  
Дата: 25.04.17 22:33
Оценка: +1
Здравствуйте, CRT, Вы писали:

CRT>Это выглядит как значение возвращенное функцией. А затем присвоенное с помощью оператора присвоения.

CRT>По идее, такое конструирование — это избыточность в языке, достаточно было оставить
CRT>
CRT>A obj(1024);
CRT>

CRT>И эта избыточность запутывает

А такой синтаксис:
int obj = 1024;

не вызывает затруднений?
--
Справедливость выше закона. А человечность выше справедливости.
Re[9]: Почему нельзя писать void ctor();
От: CRT  
Дата: 26.04.17 08:17
Оценка:
Здравствуйте, rg45, Вы писали:


R>А такой синтаксис:

R>
R>int obj = 1024;
R>

R>не вызывает затруднений?

это воспринимается как совмещение двух операций в одной строке
int obj;
obj = 1024;

выглядит как такой удобный способ сокращенной записи одной строкой: объявил и тут же присвоил.

Когда человек видит "=" он первым делом, и это естественно, думает что здесь срабатывает оператор присвоения.
Поэтому это не очевидно — то есть непонятно на первый взгляд — в таком синтаксисе, что
A obj=A(1024);

равно
A obj(1024);
Отредактировано 26.04.2017 8:24 CRT . Предыдущая версия . Еще …
Отредактировано 26.04.2017 8:24 CRT . Предыдущая версия .
Отредактировано 26.04.2017 8:21 CRT . Предыдущая версия .
Отредактировано 26.04.2017 8:20 CRT . Предыдущая версия .
Re[10]: Почему нельзя писать void ctor();
От: rg45 СССР  
Дата: 26.04.17 08:28
Оценка:
Здравствуйте, CRT, Вы писали:

CRT>Здравствуйте, rg45, Вы писали:


R>>А такой синтаксис:

R>>
R>>int obj = 1024;
R>>

R>>не вызывает затруднений?

CRT>а это не выглядит как вызов метода конструктора.


A если так?
int obj = int(1024);


CRT>это воспринимается как совмещение двух операций

CRT>
CRT>int obj;
CRT>obj = 1024;
CRT>


Что тебе мешает и в случае с пользовательским типом думать об этом как о совмещении двух операций?
A obj;
obj = A(1024);


Ты же не упускай из виду, что синтаксис спользования типов, определенных пользователем, целенаправленно делали похожим на синтаксис использования встроенных типов — чтобы максимально способствовать обобщенному программированию. Это позволяет, например, совершенно безболезненно для пользователя использовать в качестве итератора вектора как специальные классы, содержащие отладочную информацию, так и сырые указатели.
--
Справедливость выше закона. А человечность выше справедливости.
Re[11]: Почему нельзя писать void ctor();
От: CRT  
Дата: 26.04.17 08:47
Оценка:
Здравствуйте, rg45, Вы писали:


CRT>>а это не выглядит как вызов метода конструктора.


R>A если так?

R>
R>int obj = int(1024);
R>



Так — выглядит. Выглядит как вызов конструктора а потом присвоение. Только так со встроенными типами очень мало кто пишет. Зачем для инта вызывать конструктор? Да многие даже не знают что у int он как бы есть.

CRT>>это воспринимается как совмещение двух операций

CRT>>
CRT>>int obj;
CRT>>obj = 1024;
CRT>>


R>Что тебе мешает и в случае с пользовательским типом думать об этом как о совмещении двух операций?


Ничего не мешает. В том-то и проблема. Многие так и думают поначалу. А потом удивляются, почему оператор присвоения, который они определили для своего класса, не срабатывает в записи
A obj=A(1024)
Отредактировано 26.04.2017 9:07 CRT . Предыдущая версия . Еще …
Отредактировано 26.04.2017 8:54 CRT . Предыдущая версия .
Re[10]: Почему нельзя писать void ctor();
От: Erop Россия  
Дата: 26.04.17 19:55
Оценка:
Здравствуйте, CRT, Вы писали:

CRT>Поэтому это не очевидно — то есть непонятно на первый взгляд — в таком синтаксисе, что

CRT>
CRT>A obj=A(1024);
CRT>

CRT>равно
CRT>
CRT>A obj(1024);
CRT>


А они и не равны...

Попробуй скомпилировать...
class A {
    A(const A&);
public:
    int Data;
    A(int i = 0) : Data(i) {}
};

A obj = A(1024);

По С++ компилятор в такой штуке имеет право выбрасывать конструктор копии, так же, как и при RVO/NRVO, но не имеет права не проверять его доступность...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[11]: Почему нельзя писать void ctor();
От: CRT  
Дата: 27.04.17 07:42
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, CRT, Вы писали:


CRT>>Поэтому это не очевидно — то есть непонятно на первый взгляд — в таком синтаксисе, что

CRT>>
CRT>>A obj=A(1024);
CRT>>

CRT>>равно
CRT>>
CRT>>A obj(1024);
CRT>>


E>А они и не равны...


E>Попробуй скомпилировать...
E>class A {
E>    A(const A&);
E>public:
E>    int Data;
E>    A(int i = 0) : Data(i) {}
E>};

E>A obj = A(1024);

E>По С++ компилятор в такой штуке имеет право выбрасывать конструктор копии, так же, как и при RVO/NRVO, но не имеет права не проверять его доступность...

А зачем мне компилировать такое? И что оно вообще значит? То есть класс в котором объявлен но не определен конструктор копирования.
Но я попробовал скомпилировать. Ничего особого не произошло. Вызвался конструктор A(int). То есть я вижу что код A obj = A(1024); отработал так же как A obj(1024). Значит, делают вывод — равны.

Поправочка: в том что я попробовал A(const A&) был после паблик, а если его в private засунуть то не компилируется. Нужен конструктор копирования. Но с практической точки зрения. С точки зрения того, что делает программа, когда выполняется, все равно получается что A obj = A(1024) равно A obj(1024).
Отредактировано 27.04.2017 7:47 CRT . Предыдущая версия . Еще …
Отредактировано 27.04.2017 7:45 CRT . Предыдущая версия .
Re[11]: Почему нельзя писать void ctor();
От: CRT  
Дата: 27.04.17 07:57
Оценка: +1
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, CRT, Вы писали:


CRT>>Поэтому это не очевидно — то есть непонятно на первый взгляд — в таком синтаксисе, что

CRT>>
CRT>>A obj=A(1024);
CRT>>

CRT>>равно
CRT>>
CRT>>A obj(1024);
CRT>>


E>А они и не равны...


E>Попробуй скомпилировать...
E>class A {
E>    A(const A&);
E>public:
E>    int Data;
E>    A(int i = 0) : Data(i) {}
E>};

E>A obj = A(1024);

E>По С++ компилятор в такой штуке имеет право выбрасывать конструктор копии, так же, как и при RVO/NRVO, но не имеет права не проверять его доступность...

Действительно A obj = A(1024) требует наличия конструктора копирования, однако с точки зрения того что делает программа, что A obj= A(10) что A obj(10) — получается одно и то же.
Re[3]: Почему нельзя писать void ctor();
От: N. I.  
Дата: 27.04.17 17:03
Оценка:
Максим Рогожин:

МР>Мужики, еще немного занудства) Учитывая все сказанное, а почему тогда разрешен следующий синтаксис?


МР>
A obj = A(1024);

МР>Глядя на эту запись можно подумать, что здесь конструктор именно зовется напрямую и возвращает значение

Начнём с того, что A(1024) — это вовсе не обычный function call, а явное преобразование типа в функциональной нотации, которое в данном случае эквивалентно static_cast<A>(1024) и выполняется посредством конструктора. Существует масса других случаев, когда преобразование вида T(x) не использует какие-либо конструкторы. Например, short(1024) преобразует значение типа int к short без применения конструкторов. Добавив в класс A конструктор A(int), ты определил пользовательское преобразование из int в A, что и даёт тебе возможность использовать выражение вроде A(1024). Явные преобразования типов и обычные вызовы функций — это разные вещи, регламентируемые разными правилами, и то, что их синтаксис похож, не должно создавать излишних иллюзий насчёт их похожести во всём.
Re[10]: Почему нельзя писать void ctor();
От: N. I.  
Дата: 27.04.17 17:10
Оценка: 2 (1) +1
CRT:

CRT>Поэтому это не очевидно — то есть непонятно на первый взгляд — в таком синтаксисе, что

CRT>
A obj=A(1024);

CRT>равно
CRT>
A obj(1024);

C++ — это вообще не тот язык, где всё становится очевидно после пары дней беглого знакомства с его правилами. Тут пока досконально не узучишь детали, не поймёшь, как оно работает.

Нечто вроде

T x = T(arg);

не является какой-то специальной конструкцией языка, это всего лишь частный случай инициализации вида

T x = a;

где в качестве a выступает некое выражение T(arg).

В отличие от общей direct-initialization

T x(a);

copy-initialization

T x = a;

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

std::string const s = "text";
std::vector<int> v(42);

Здесь литерал "text" и объект s по сути представляют одну и ту же сущность — строку text. Различия состоят лишь в способе хранения этой строки и наборе доступных операций над ней.

v непосредственно после своей инициализации хранит последовательность из 42-х нулей, и эту последовательность из 42-х нулей довольно трудно назвать близкой по смыслу к самому числу 42. Конструктор vector-а, где мы указываем размер, объявлен как explicit, что как раз запрещает использование

std::vector<int> v = 42;

поэтому здесь мы вынуждены использовать direct-initialization, тем самым указывая на потенциальную возможность большого различия между смыслом того, что мы иницилизируем, и смыслом того, чем мы инициализируем. Своеобразная универсальность direct-initialization не делает её всегда лучше, чем copy-initialization, точно так же, как универсальность (T)arg не делает данный вид преобразования всегда лучше, чем более ограниченный static_cast<T>(arg).

Я не вижу особого смысла в использовании

T x = T(a);

вместо

T x(a);

где T — это классовый тип. Насколько я вижу, по правилам С++17 эти два варианта полностью идентичны. Но я и не вижу каких-либо причин запрещать первый вариант на уровне языка. Как минимум, нечто подобное вполне могло бы получиться в виде частного случая при инстанцировании какого-нибудь шаблона или раскрытии какого-нибудь макроса, и было бы странно если б оно внезапно перестало собираться.
Отредактировано 27.04.2017 17:30 N. I. . Предыдущая версия .
Re[4]: Почему нельзя писать void ctor();
От: CRT  
Дата: 27.04.17 20:17
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Максим Рогожин:


МР>>Мужики, еще немного занудства) Учитывая все сказанное, а почему тогда разрешен следующий синтаксис?


МР>>
NI>A obj = A(1024);
NI>

МР>>Глядя на эту запись можно подумать, что здесь конструктор именно зовется напрямую и возвращает значение

NI>Начнём с того, что A(1024) — это вовсе не обычный function call, а явное преобразование типа в функциональной нотации, которое в данном случае эквивалентно static_cast<A>(1024) и выполняется посредством конструктора. Существует масса других случаев, когда преобразование вида T(x) не использует какие-либо конструкторы. Например, short(1024) преобразует значение типа int к short без применения конструкторов. Добавив в класс A конструктор A(int), ты определил пользовательское преобразование из int в A, что и даёт тебе возможность использовать выражение вроде A(1024). Явные преобразования типов и обычные вызовы функций — это разные вещи, регламентируемые разными правилами, и то, что их синтаксис похож, не должно создавать излишних иллюзий насчёт их похожести во всём.


Хорошо, A(1024) это преобразование типа.
Но вот это
A obj = A(1024,"ъъъ");

уже явно не выглядит как преобразование типа, а именно как вызов конструктора напрямую , который возвращает значение.
А по мне так конструктор с одним параметром — это частный случай конструктора с N параметрами.
Re[5]: Почему нельзя писать void ctor();
От: N. I.  
Дата: 27.04.17 22:11
Оценка:
CRT:

CRT>Хорошо, A(1024) это преобразование типа.

CRT>Но вот это
CRT>
A obj = A(1024,"ъъъ");

CRT>уже явно не выглядит как преобразование типа

Однако, формально это тоже относится к преобразованию типа — Explicit type conversion (functional notation) — от списка из нескольких выражений.

CRT>а именно как вызов конструктора напрямую , который возвращает значение.


Конструктор только инициализирует объект, но выделением того участка памяти, которая становится объектом (и размер которой мы можем узнать с помощью sizeof), он не занимается. Деструктор объекта он обычно тоже не вызывает. Как и определение переменной A a(1024,"ъъъ"); , выражение A(1024,"ъъъ") приводит не только к вызову конструктора, но ещё и резервированию памяти под создаваемый объект, а также к вызову деструктора в какой-то момент, а стало быть, это всё-таки более высокоуровневая штука, чем некий простой вызов конструктора. Если нужно выражение, которое по сути не делает ничего, кроме вызова конструктора (с предшествующей инициализацией его параметров), то для этого надо использовать что-то вроде ::new((void *)pointer) A(1024,"ъъъ"), где pointer указывает на участок памяти, который мы собираемся отвести под объект. Хотя формально тут всё же есть ещё вызов функции operator new, эта версия operator new реально ничего не делает, а потому фактическая функциональность такого выражения сводится к вызову конструктора.

CRT>А по мне так конструктор с одним параметром — это частный случай конструктора с N параметрами.


А как тебе вот такой пример?

#include <iostream>

struct A
{
    A(A &&) = delete;
    int n;
};

A f()
{
    return A{1024};
}

int main()
{
    A obj = A(f());
    std::cout << obj.n << std::endl;
}

По правилам C++17 здесь нет ни одного вызова конструктора A (собственно, такой A даже не имеет ни одного конструктора, который было бы допустимо вызывать), и, тем не менее, эта программа вполне рабочая.
Отредактировано 27.04.2017 22:21 N. I. . Предыдущая версия .
Re[6]: Почему нельзя писать void ctor();
От: uzhas Ниоткуда  
Дата: 28.04.17 09:51
Оценка:
Здравствуйте, N. I., Вы писали:

NI>А как тебе вот такой пример?

...
NI>По правилам C++17 здесь нет ни одного вызова конструктора A (собственно, такой A даже не имеет ни одного конструктора, который было бы допустимо вызывать), и, тем не менее, эта программа вполне рабочая.

а можно пояснить, почему нет вызова конструктора?
вот тут видно, что деструктор вызывается: https://wandbox.org/permlink/UB1kdA5fS0h2Mo5Q
значит, был вызов и конструктора?
Re[7]: Почему нельзя писать void ctor();
От: night beast СССР  
Дата: 28.04.17 09:56
Оценка:
Здравствуйте, uzhas, Вы писали:

U>а можно пояснить, почему нет вызова конструктора?

U>вот тут видно, что деструктор вызывается: https://wandbox.org/permlink/UB1kdA5fS0h2Mo5Q
U>значит, был вызов и конструктора?

является ли aggregate initialization вызовом конструктора?
Re[8]: Почему нельзя писать void ctor();
От: uzhas Ниоткуда  
Дата: 28.04.17 10:11
Оценка:
Здравствуйте, night beast, Вы писали:

NB>является ли aggregate initialization вызовом конструктора?


вопрос на миллион!
Re[12]: Почему нельзя писать void ctor();
От: Erop Россия  
Дата: 28.04.17 20:35
Оценка:
Здравствуйте, CRT, Вы писали:

E>>Попробуй скомпилировать...
E>>class A {
E>>    A(const A&);
E>>public:
E>>    int Data;
E>>    A(int i = 0) : Data(i) {}
E>>};

E>>A obj = A(1024);

CRT>А зачем мне компилировать такое? И что оно вообще значит?
Что конструктор копии A является private...

CRT>Но я попробовал скомпилировать. Ничего особого не произошло. Вызвался конструктор A(int). То есть я вижу что код A obj = A(1024); отработал так же как A obj(1024). Значит, делают вывод — равны.

C private конструктором копии?.. А каким компилятором?

CRT>Поправочка: в том что я попробовал A(const A&) был после паблик, а если его в private засунуть то не компилируется.

Так в этом и состоит смысл жеж

Смотри, что я утверждал:
E>>По С++ компилятор в такой штуке имеет право выбрасывать конструктор копии, так же, как и при RVO/NRVO, но не имеет права не проверять его доступность...

Дальше, если хочешь разобраться, то разбираешься. Я пытался помочь в этом, но можешь просто прочитать стандарт.
Если не хочешь, то и обсуждать нечего...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: Почему нельзя писать void ctor();
От: CRT  
Дата: 28.04.17 20:39
Оценка: +1
Здравствуйте, Erop, Вы писали:


CRT>>Поправочка: в том что я попробовал A(const A&) был после паблик, а если его в private засунуть то не компилируется.

E>Так в этом и состоит смысл жеж

да я понял что в этом смысл. Просто сразу не заметил что A(const A&) закрыт.
Re[12]: Почему нельзя писать void ctor();
От: Erop Россия  
Дата: 28.04.17 20:40
Оценка:
Здравствуйте, CRT, Вы писали:

E>>По С++ компилятор в такой штуке имеет право выбрасывать конструктор копии, так же, как и при RVO/NRVO, но не имеет права не проверять его доступность...


CRT>Действительно A obj = A(1024) требует наличия конструктора копирования, однако с точки зрения того что делает программа, что A obj= A(10) что A obj(10) — получается одно и то же.


Ну так это же оптимизация просто.

В этом смысле и программы
std::cout << 2+2;
и
std::cout << 4;
делают одно и тоже одинаковым кодом, но логически написано разное.

В этом смысле
A a = A(1234);
не особо понятно чем отличается.
A(1234) -- это выражение, типа A.
Компилятор может такую конструкцию оптимизировать до
A a(1234);
это прямо разрешено в стандарте.
В чём ты тут вообще усматриваешь принципиальную разницу с
std::cout << 4;
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Почему нельзя писать void ctor();
От: N. I.  
Дата: 28.04.17 21:37
Оценка:
uzhas:

U>а можно пояснить, почему нет вызова конструктора?


A{1024} выполняет aggregate initialization, которая инициализирует объект типа A без привлечения конструкторов. По новым правилам результат A{1024}, return object функции f, результат преобразования A(f()) и obj — это один и тот же объект; никаких дополнительных объектов типа A нигде не создаётся.

Отдельного упоминания заслуживает вот такой пример:

#include <iostream>

void *ptr;

struct X
{
    X() { ptr = this; }
};

struct A
{
    A(A &&) = delete;
    X x;

private:
    A(A &) = default;
};

A f()
{
    return A{};
}

int main()
{
    A obj = A(f());
    std::cout << (&obj == ptr) << std::endl;
    std::cout << &obj << std::endl;
    std::cout << ptr << std::endl;
}

Здесь наличие конструктора A(A &) позволяет делать копию объекта при возврате из функции f (то, что конструктор приватный и не может принимать rvalue типа A, в данном случае роли не играет), отчего равенство (&obj == ptr) может не соблюдаться. Хотя gcc 8 пока что имеет наглость создавать копию и при полном отсутствии non-deleted copy/move-конструкторов, что, насколько я вижу, является нарушением правил.

U>вот тут видно, что деструктор вызывается: https://wandbox.org/permlink/UB1kdA5fS0h2Mo5Q

U>значит, был вызов и конструктора?

Объект классового типа может быть инициализирован без использования конструктора, это никак не мешает ему быть уничтоженным с помощью деструктора.
Отредактировано 28.04.2017 21:41 N. I. . Предыдущая версия .
Re[7]: Почему нельзя писать void ctor();
От: Максим Рогожин Россия  
Дата: 17.10.17 16:56
Оценка:
Здравствуйте, rg45, Вы писали:

R>для создания rvalue объекта во временной области памяти:

const auto& obj = A(1024);
aFunction(A(1024) + A(42));


А можно уточнить где будут созданы объекты A(1024) и A(42)? Что за временная область памяти? Это понятие есть в стандарте? Или то где будут
созданы эти два объекта это implementation details?
Re: Почему нельзя писать void ctor();
От: Pzz Россия https://github.com/alexpevzner
Дата: 17.10.17 17:26
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>Отличается ли из-за этого низкоуровневая организация вызова этой функции (организация стека, пролог, эпилог)?


Нет, просто договоренность такая, что конструктор не имеет явно указанного возвращаемого типа. Конструктор в любом случае — особая функция, и компилятор понимает, как для него организовывать стек и прочий пролог.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.