Здравствуйте, _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, если нас особо не волнует тип созданной переменной.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Здравствуйте, VTT, Вы писали:
VTT>Если f возвращает A, то может иметь место Copy elision, и все четыре записи окажутся равносильны. VTT>Если f возвращает не A, то a3 и a4 будут иметь этот тип (или производный), и вторые две записи не будут равносильны первым двум.
Это хорошо. С логикой у меня всё в порядке
_NN>>Если это так, то имеет ли смысл писать auto a = ... вместо auto a(...) ? VTT>Имеет смысл писать auto a(...) вместо auto a = ... чтобы потом было проще искать настоящие вызовы оператора присваивания. VTT>Имеет смысл писать auto, если нас особо не волнует тип созданной переменной.
Вот я часто вижу код:
Здравствуйте, _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 = ... унаследован от С, где были только встроенные операторы присваивания.
И неявное преобразование такой записи в вызов конструктора — по сути просто костыль для обеспечения совместимости с С.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Я бы выбрал первый вариант в силу привычки минимизировать уровень вложенности скобок для лучшей читаемости. Если в ... есть ещё скобки, то в первом случае получаем одинарную вложенность, а во втором — уже двойную.
Здравствуйте, so5team, Вы писали:
S>Может чтобы не наступать на те же грабли с неоднозначностью: S>
auto a(B());
S>где a может выглядеть как прототип функции с автоматическим выводом типа значения и аргументом типа B()?
Учитывая отсутствие копирование выглядит, я готов на эту жертву.
Для таких редких случаев есть двойные скобки.
auto a((B()));
Или фигурные, правда не всегда желаемый результат.
Здравствуйте, _NN_, Вы писали:
_NN>Увы поиск по форуму ничего не дал. _NN>Хочется прояснить ситуацию с инициализацией через auto.
auto обозначает "вывести тип из выражения и очистить его от ссылочности" (std::decay делает то же самое явно).
Всю ссылочность надо писать явно (auto&, auto const&). Либо отказываться от распада (auto&&).
Поэтому писать A x(expr) или auto x(expr), где expr возвращает возможно-ссылочный тип A, — это одно и то же.
Что касается выбора между инициализацией присваиванием и явным конструктором, — это дело вкуса, во-первых, и наличие подходящих конструкторов, во-вторых.
Компилятор имеет право сократить конструктор копирования или перемещения.
Но формально, если там подразумевается копирование или перемещение, то оно должно быть доступно.
Для shared_ptr конструктор копирования доступен, поэтому можно обоими способами написать.
Здравствуйте, Кодт, Вы писали:
К>Для shared_ptr конструктор копирования доступен, поэтому можно обоими способами написать.
Доступен то да, но как-то обидно если компилятор всё таки им воспользуется и создаст лишний объект на пустом месте, хотя старым способом было бы без него.
_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();
}
Здравствуйте, N. I., Вы писали:
NB>>это как? а если f в другой единице трансляции будет?
NI>Реализация каким-нибудь образом передаёт в f адрес целевого объекта, и дальше f занимается его конструированием. Псевдокод:
это работает только для не POD типов? или для всех?
если для всех, то как будет работать для функций, скомпиленных на других языках (например си)?
Здравствуйте, night beast, Вы писали:
NI>>Реализация каким-нибудь образом передаёт в f адрес целевого объекта, и дальше f занимается его конструированием. Псевдокод:
NB>если для всех, то как будет работать для функций, скомпиленных на других языках (например си)?
Про то как это работает написано в ABI платформы. Соответственно, ABI должны реализовать все языки, которые хотят уметь общаться с друг-другом. То есть для C и C++ тут проблем никаких нет — компилятор делает ровно так, как написано в документации.
И в ней как раз перечислены все допустимые способы. Про главный из них N. I. уже рассказал: просто в функцию передаётся скрытый аргумент, в котором записан адрес по которому нужно разместить объект. В то же время этот способ не обязательно единственно возможный: в некоторых ситуациях от реализации может быть потребовано сделать так, чтобы возвращаемая структура была размещена не в памяти, а, например, размазана по регистрам (у которых которых к тому же нет адреса, из-за чего передавать в функцию в скрытом параметре тоже оказывается нечего).
NB>это работает только для не POD типов? или для всех?
ABI описывает правила передачи для всех типов. А какое именно правило будет применено к конкретному типу — зависит уже только от него.
Но если посмотреть существующие ABI, то в первом приближении общее правило таково: если структуру можно эффективно вернуть через регистры, то возвращать её следует через регистры, иначе же следует использовать схему с заранее приготовленной памятью и скрытым параметром. А является ли структура POD или не-POD — это в большинстве случаев совершенно всё равно.
Если тебе действительно интересны конкретные детали, то можешь для примера посмотреть содержимое раздела 3.2.3 в этом документе. Там как раз всё это описано формальным языком.
Здравствуйте, night beast, Вы писали:
NI>>Реализация каким-нибудь образом передаёт в f адрес целевого объекта, и дальше f занимается его конструированием. Псевдокод:
NB>это работает только для не POD типов? или для всех? NB>если для всех, то как будет работать для функций, скомпиленных на других языках (например си)?
А как на сях возвращаются структуры? К.м.к. это так всегда и делалось: вызывающая сторона передаёт адрес буфера, функция суёт туда данные.
Иначе — только если структура достаточно маленькая, чтобы вернуть её в регистре.
Здравствуйте, N. I., Вы писали:
NI>_NN_:
К>>>Для shared_ptr конструктор копирования доступен, поэтому можно обоими способами написать. _NN>>Доступен то да, но как-то обидно если компилятор всё таки им воспользуется и создаст лишний объект на пустом месте, хотя старым способом было бы без него.
NI>А такие компиляторы с поддержкой C++11/14 вообще есть? Если эта формальная возможность не даёт покоя, то можно использовать
NI>auto &&x = prvalue;
Тут тоже видимо есть подвох иначе бы всегда можно было так писать, разве нет ?
_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();
никакой оптимизации, скорее всего, не получится. Разница будет лишь в том случае, когда созданный объект неперемещаемый и второй вариант компилятору из-за этого не нравится.
Здравствуйте, N. I., Вы писали:
NI>_NN_:
NI>>>auto &&x = prvalue; _NN>>Тут тоже видимо есть подвох иначе бы всегда можно было так писать, разве нет ?
NI>Подвох лишь в том, что гарантия отсутствия копирования/перемещения будет применена лишь к поведению абстрактной машины, при этом стандарт не запрещает компилятору преобразовывать код на C++ в сколь угодно неэффективный машинный код с тем же observable behavior. Например,
Разве это же нельзя сказать и про такой же простой код
A a(prvalue);
Видимо где-то есть существенная разница между этими вариантами и в каком-то случае даст не тот результат, но не могу понять где.
_NN_:
NI>>Подвох лишь в том, что гарантия отсутствия копирования/перемещения будет применена лишь к поведению абстрактной машины, при этом стандарт не запрещает компилятору преобразовывать код на C++ в сколь угодно неэффективный машинный код с тем же observable behavior. Например,
_NN>Разве это же нельзя сказать и про такой же простой код
Сказанное относится к любому коду на C++. Как ни крути, приходится либо полагаться на то, что разработчики компиляторов реализуют разумное преобразование исходного кода в целевой, либо переходить на что-то вроде ассемблера.
_NN>Видимо где-то есть существенная разница между этими вариантами и в каком-то случае даст не тот результат, но не могу понять где.
Ну, ежели найдёшь пример, когда вариант без ссылки работает, а вариант со ссылкой — нет, дай знать