Копирование при auto инициализации
От: _NN_ www.nemerleweb.com
Дата: 27.11.16 11:50
Оценка:
Увы поиск по форуму ничего не дал.
Хочется прояснить ситуацию с инициализацией через auto.

Являются ли a1 и a3 равносильными , а также a2 и a4 ?
Если это так, то имеет ли смысл писать auto a = ... вместо auto a(...) ?
    A a1 = f("1");
    
    A a2(f("2"));
    
    auto a3 = f("3");

    auto a4(f("3"));
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Копирование при auto инициализации
От: VTT http://vtt.to
Дата: 27.11.16 12:11
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Увы поиск по форуму ничего не дал.

_NN>Хочется прояснить ситуацию с инициализацией через auto.

_NN>Являются ли a1 и a3 равносильными , а также a2 и a4 ?

_NN>
_NN>    A a1 = f("1");
    
_NN>    A a2(f("2"));
    
_NN>    auto a3 = f("3");

_NN>    auto a4(f("3"));
_NN>

Если f возвращает A, то может иметь место Copy elision, и все четыре записи окажутся равносильны.
Если f возвращает не A, то a3 и a4 будут иметь этот тип (или производный), и вторые две записи не будут равносильны первым двум.

_NN>Если это так, то имеет ли смысл писать auto a = ... вместо auto a(...) ?

Имеет смысл писать auto a(...) вместо auto a = ... чтобы потом было проще искать настоящие вызовы оператора присваивания.
Имеет смысл писать auto, если нас особо не волнует тип созданной переменной.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[2]: Копирование при auto инициализации
От: _NN_ www.nemerleweb.com
Дата: 27.11.16 12:13
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Если f возвращает A, то может иметь место Copy elision, и все четыре записи окажутся равносильны.

VTT>Если f возвращает не A, то a3 и a4 будут иметь этот тип (или производный), и вторые две записи не будут равносильны первым двум.
Это хорошо. С логикой у меня всё в порядке

_NN>>Если это так, то имеет ли смысл писать auto a = ... вместо auto a(...) ?

VTT>Имеет смысл писать auto a(...) вместо auto a = ... чтобы потом было проще искать настоящие вызовы оператора присваивания.
VTT>Имеет смысл писать auto, если нас особо не волнует тип созданной переменной.
Вот я часто вижу код:

auto a = make_shared<A>(...);

Почему не пишут как надо тогда?
auto a(make_shared<A>(...));
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Копирование при auto инициализации
От: VTT http://vtt.to
Дата: 27.11.16 12:19
Оценка: +2
Здравствуйте, _NN_, Вы писали:

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


VTT>>Если f возвращает A, то может иметь место Copy elision, и все четыре записи окажутся равносильны.

VTT>>Если f возвращает не A, то a3 и a4 будут иметь этот тип (или производный), и вторые две записи не будут равносильны первым двум.
_NN>Это хорошо. С логикой у меня всё в порядке

_NN>>>Если это так, то имеет ли смысл писать auto a = ... вместо auto a(...) ?

VTT>>Имеет смысл писать auto a(...) вместо auto a = ... чтобы потом было проще искать настоящие вызовы оператора присваивания.
VTT>>Имеет смысл писать auto, если нас особо не волнует тип созданной переменной.
_NN>Вот я часто вижу код:

_NN>
_NN>auto a = make_shared<A>(...);
_NN>

_NN>Почему не пишут как надо тогда?
_NN>
_NN>auto a(make_shared<A>(...));
_NN>


плюрализм

PS я тут подумал, ведь синтаксис A a = ... унаследован от С, где были только встроенные операторы присваивания.
И неявное преобразование такой записи в вызов конструктора — по сути просто костыль для обеспечения совместимости с С.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Отредактировано 27.11.2016 12:59 VTT . Предыдущая версия .
Re[3]: Копирование при auto инициализации
От: so5team https://stiffstream.com
Дата: 27.11.16 14:26
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Почему не пишут как надо тогда?

_NN>
_NN>auto a(make_shared<A>(...));
_NN>


Может чтобы не наступать на те же грабли с неоднозначностью:
auto a(B());

где a может выглядеть как прототип функции с автоматическим выводом типа значения и аргументом типа B()?
Re[3]: Копирование при auto инициализации
От: N. I.  
Дата: 27.11.16 15:12
Оценка: +1
_NN_:

_NN>Вот я часто вижу код:


_NN>
_NN>auto a = make_shared<A>(...);
_NN>

_NN>Почему не пишут как надо тогда?
_NN>
auto a(make_shared<A>(...));

Я бы выбрал первый вариант в силу привычки минимизировать уровень вложенности скобок для лучшей читаемости. Если в ... есть ещё скобки, то в первом случае получаем одинарную вложенность, а во втором — уже двойную.
Re[4]: Копирование при auto инициализации
От: _NN_ www.nemerleweb.com
Дата: 27.11.16 19:14
Оценка:
Здравствуйте, so5team, Вы писали:

S>Может чтобы не наступать на те же грабли с неоднозначностью:

S>
auto a(B());

S>где a может выглядеть как прототип функции с автоматическим выводом типа значения и аргументом типа B()?
Учитывая отсутствие копирование выглядит, я готов на эту жертву.

Для таких редких случаев есть двойные скобки.
auto a((B()));


Или фигурные, правда не всегда желаемый результат.
auto a{B()};
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Копирование при auto инициализации
От: Кодт Россия  
Дата: 27.11.16 19:56
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Увы поиск по форуму ничего не дал.

_NN>Хочется прояснить ситуацию с инициализацией через auto.

auto обозначает "вывести тип из выражения и очистить его от ссылочности" (std::decay делает то же самое явно).
Всю ссылочность надо писать явно (auto&, auto const&). Либо отказываться от распада (auto&&).

Поэтому писать A x(expr) или auto x(expr), где expr возвращает возможно-ссылочный тип A, — это одно и то же.

Что касается выбора между инициализацией присваиванием и явным конструктором, — это дело вкуса, во-первых, и наличие подходящих конструкторов, во-вторых.
Компилятор имеет право сократить конструктор копирования или перемещения.
Но формально, если там подразумевается копирование или перемещение, то оно должно быть доступно.

Для shared_ptr конструктор копирования доступен, поэтому можно обоими способами написать.
Перекуём баги на фичи!
Re[2]: Копирование при auto инициализации
От: _NN_ www.nemerleweb.com
Дата: 27.11.16 20:34
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Для shared_ptr конструктор копирования доступен, поэтому можно обоими способами написать.

Доступен то да, но как-то обидно если компилятор всё таки им воспользуется и создаст лишний объект на пустом месте, хотя старым способом было бы без него.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Копирование при auto инициализации
От: N. I.  
Дата: 28.11.16 10:47
Оценка: 3 (1)
_NN_:

К>>Для shared_ptr конструктор копирования доступен, поэтому можно обоими способами написать.

_NN>Доступен то да, но как-то обидно если компилятор всё таки им воспользуется и создаст лишний объект на пустом месте, хотя старым способом было бы без него.

А такие компиляторы с поддержкой C++11/14 вообще есть? Если эта формальная возможность не даёт покоя, то можно использовать

auto &&x = prvalue;

В C++17 ввели guaranteed copy elision, допускающее

auto x = prvalue;

при отсутствии возможности перемещения. 7-й g++ уже умеет компилировать вот такое:

struct C
{
    explicit C(int) {}
    C(C &&) = delete;
};

C f()
{
    return C(0);
}

int main()
{
    auto x = f();
}

http://melpon.org/wandbox/permlink/hC8wX2m79vUVGU7E
Re[4]: Копирование при auto инициализации
От: night beast СССР  
Дата: 28.11.16 11:30
Оценка:
Здравствуйте, N. I., Вы писали:

NI>при отсутствии возможности перемещения. 7-й g++ уже умеет компилировать вот такое:


NI>
struct C
NI>{
NI>    explicit C(int) {}
NI>    C(C &&) = delete;
NI>};

NI>C f()
NI>{
NI>    return C(0);
NI>}

NI>int main()
NI>{
NI>    auto x = f();
NI>}



это как? а если f в другой единице трансляции будет?
Re[5]: Копирование при auto инициализации
От: N. I.  
Дата: 28.11.16 12:18
Оценка: +1
night beast:

NB>это как? а если f в другой единице трансляции будет?


Реализация каким-нибудь образом передаёт в f адрес целевого объекта, и дальше f занимается его конструированием. Псевдокод:

struct C
{
    explicit C(int) {}
    C(C &&) = delete;
};

// C f()
__function f(__return void *__ret_ptr)
{
    // return C(0);
    ::new(__ret_ptr) C(0);
}

int main()
{
    // auto x = f();
    C x __no_initialization;
    f(__address_of(x));
}
Re[6]: Копирование при auto инициализации
От: night beast СССР  
Дата: 28.11.16 12:29
Оценка:
Здравствуйте, N. I., Вы писали:

NB>>это как? а если f в другой единице трансляции будет?


NI>Реализация каким-нибудь образом передаёт в f адрес целевого объекта, и дальше f занимается его конструированием. Псевдокод:


это работает только для не POD типов? или для всех?
если для всех, то как будет работать для функций, скомпиленных на других языках (например си)?
Re[7]: Копирование при auto инициализации
От: watchmaker  
Дата: 28.11.16 14:05
Оценка: 3 (1)
Здравствуйте, night beast, Вы писали:

NI>>Реализация каким-нибудь образом передаёт в f адрес целевого объекта, и дальше f занимается его конструированием. Псевдокод:


NB>если для всех, то как будет работать для функций, скомпиленных на других языках (например си)?


Про то как это работает написано в ABI платформы. Соответственно, ABI должны реализовать все языки, которые хотят уметь общаться с друг-другом. То есть для C и C++ тут проблем никаких нет — компилятор делает ровно так, как написано в документации.
И в ней как раз перечислены все допустимые способы. Про главный из них N. I. уже рассказал: просто в функцию передаётся скрытый аргумент, в котором записан адрес по которому нужно разместить объект. В то же время этот способ не обязательно единственно возможный: в некоторых ситуациях от реализации может быть потребовано сделать так, чтобы возвращаемая структура была размещена не в памяти, а, например, размазана по регистрам (у которых которых к тому же нет адреса, из-за чего передавать в функцию в скрытом параметре тоже оказывается нечего).

NB>это работает только для не POD типов? или для всех?

ABI описывает правила передачи для всех типов. А какое именно правило будет применено к конкретному типу — зависит уже только от него.
Но если посмотреть существующие ABI, то в первом приближении общее правило таково: если структуру можно эффективно вернуть через регистры, то возвращать её следует через регистры, иначе же следует использовать схему с заранее приготовленной памятью и скрытым параметром. А является ли структура POD или не-POD — это в большинстве случаев совершенно всё равно.

Если тебе действительно интересны конкретные детали, то можешь для примера посмотреть содержимое раздела 3.2.3 в этом документе. Там как раз всё это описано формальным языком.
Re[7]: Копирование при auto инициализации
От: Кодт Россия  
Дата: 28.11.16 14:06
Оценка:
Здравствуйте, night beast, Вы писали:

NI>>Реализация каким-нибудь образом передаёт в f адрес целевого объекта, и дальше f занимается его конструированием. Псевдокод:


NB>это работает только для не POD типов? или для всех?

NB>если для всех, то как будет работать для функций, скомпиленных на других языках (например си)?

А как на сях возвращаются структуры? К.м.к. это так всегда и делалось: вызывающая сторона передаёт адрес буфера, функция суёт туда данные.
Иначе — только если структура достаточно маленькая, чтобы вернуть её в регистре.
Перекуём баги на фичи!
Re[4]: Копирование при auto инициализации
От: _NN_ www.nemerleweb.com
Дата: 29.11.16 04:43
Оценка:
Здравствуйте, N. I., Вы писали:

NI>_NN_:


К>>>Для shared_ptr конструктор копирования доступен, поэтому можно обоими способами написать.

_NN>>Доступен то да, но как-то обидно если компилятор всё таки им воспользуется и создаст лишний объект на пустом месте, хотя старым способом было бы без него.

NI>А такие компиляторы с поддержкой C++11/14 вообще есть? Если эта формальная возможность не даёт покоя, то можно использовать


NI>auto &&x = prvalue;

Тут тоже видимо есть подвох иначе бы всегда можно было так писать, разве нет ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: Копирование при auto инициализации
От: N. I.  
Дата: 29.11.16 11:31
Оценка:
_NN_:

NI>>auto &&x = prvalue;

_NN>Тут тоже видимо есть подвох иначе бы всегда можно было так писать, разве нет ?

Подвох лишь в том, что гарантия отсутствия копирования/перемещения будет применена лишь к поведению абстрактной машины, при этом стандарт не запрещает компилятору преобразовывать код на C++ в сколь угодно неэффективный машинный код с тем же observable behavior. Например,

struct A
{
    char data[1024];
};

A const &f();

int main()
{
    A const &a = f();
    std::cout << a.data[0] << std::endl;
}

теоретически может быть вычислено с использованием копирования всей структуры, как будто код был бы

int main()
{
    A const *pa = &f();
    A a;
    std::memcpy(&a, pa, sizeof(A));
    std::cout << *reinterpret_cast<char *>(&a) << std::endl;
}

без применения какой-либо дальнейшей оптимизации. Несмотря на то, что в исходном примере мы работаем со ссылкой, никто не запрещает копировать объект, на который она ссылается, пока observable behavior не меняется. Аналогично с std::shared_ptr:

void use_my_shared_pointer(std::shared_ptr<T> &);
auto &&ptr = std::shared_ptr<T>(new T);
use_my_shared_pointer(ptr);

В рамках observable behavior нет никакого отличия между присутствием или отсутствием копирования/перемещения объекта, созданного выражением std::shared_ptr<T>(new T). Поэтому, если компилятор скопирует его 100500 раз и передаст 100500-ю копию в функцию use_my_shared_pointer, то, с точки зрения стандарта, ничего необычного здесь не будет. Но реально такой компилятор, понятное дело, никто разрабатывать не станет.

При использовании

auto &&x = prvalue_of_class_type();

вместо

auto x = prvalue_of_class_type();

никакой оптимизации, скорее всего, не получится. Разница будет лишь в том случае, когда созданный объект неперемещаемый и второй вариант компилятору из-за этого не нравится.
Re[6]: Копирование при auto инициализации
От: _NN_ www.nemerleweb.com
Дата: 29.11.16 19:52
Оценка:
Здравствуйте, N. I., Вы писали:

NI>_NN_:


NI>>>auto &&x = prvalue;

_NN>>Тут тоже видимо есть подвох иначе бы всегда можно было так писать, разве нет ?

NI>Подвох лишь в том, что гарантия отсутствия копирования/перемещения будет применена лишь к поведению абстрактной машины, при этом стандарт не запрещает компилятору преобразовывать код на C++ в сколь угодно неэффективный машинный код с тем же observable behavior. Например,


Разве это же нельзя сказать и про такой же простой код
A a(prvalue);


Видимо где-то есть существенная разница между этими вариантами и в каком-то случае даст не тот результат, но не могу понять где.

Ну и ещё варианты
auto&& x ( prvalue );
auto&& x { prvalue };
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[7]: Копирование при auto инициализации
От: N. I.  
Дата: 30.11.16 14:00
Оценка:
_NN_:

NI>>Подвох лишь в том, что гарантия отсутствия копирования/перемещения будет применена лишь к поведению абстрактной машины, при этом стандарт не запрещает компилятору преобразовывать код на C++ в сколь угодно неэффективный машинный код с тем же observable behavior. Например,


_NN>Разве это же нельзя сказать и про такой же простой код


Сказанное относится к любому коду на C++. Как ни крути, приходится либо полагаться на то, что разработчики компиляторов реализуют разумное преобразование исходного кода в целевой, либо переходить на что-то вроде ассемблера.

_NN>Видимо где-то есть существенная разница между этими вариантами и в каком-то случае даст не тот результат, но не могу понять где.


Ну, ежели найдёшь пример, когда вариант без ссылки работает, а вариант со ссылкой — нет, дай знать
Отредактировано 30.11.2016 14:03 N. I. . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.