То, что вы видите, является результатом допустимой стандартной оптимизации, когда компилятор избегает создания временных. Компилятору разрешено заменять конструкцию и назначение простой конструкцией даже при наличии побочных эффектов (например, IO в вашем примере).
Но тот факт, что программа плохо сформирована или нет, не должен зависеть от ситуации, когда компилятор выполняет эту оптимизацию или нет. Вот почему
Foo f = Foo(3);
требуется конструктор копирования. И
Foo f(3);
нет. Хотя это, вероятно, приведет к тому же двоичному коду.
Цитата из 12.8.15
При соблюдении определенных критериев
реализация может опустить
копировать конструкцию объекта класса,
даже если конструктор копирования и / или
деструктор для объекта есть сторона
последствия. В таких случаях
реализация относится к источнику и
цель пропущенной операции копирования
а просто два разных способа
ссылаясь на тот же объект, и
разрушение этого объекта происходит в
поздние времена, когда два
объекты были бы уничтожены
без оптимизации.111) Это
исключение операций копирования
разрешено в следующем
обстоятельства (которые могут быть объединены
исключить несколько копий):
- в
оператор возврата в функции с
тип возвращаемого класса, когда выражение
это имя энергонезависимой
автоматический объект с тем же
cv-неквалифицированный тип как функция
тип возврата, операция копирования может быть
опущено при построении автоматического
объект непосредственно в функцию
возвращаемое значение
- при временном уроке
объект, который не был связан с
ссылка (12.2) будет скопирована в
объект класса с тем же
CV-неквалифицированный тип, копия
операция может быть опущена
строительство временного объекта
прямо в цель
опущенная копия
См. Также «Оптимизация возвращаемого значения».