(Этот вопрос вдохновлен Беседа Николая Йосуттиса на CppCon 2017.)
Рассмотрим следующий исходный файл (для объекта, а не для полной программы):
#include <string>
class C {
std::string s_;
public:
C(std::string s) : s_(s) { };
void bar();
};
void foo() {
std::string hello { "The quick brown fox jumped over the lazy dog" };
C c { hello };
c.bar();
}
И его результат компиляции на GodBolt.
Даже с -O2
(и даже с -O3
Кажется, строковый конструктор вызывается три раза. В частности, s
построен, используется только для построения s_
затем разрушен. Мои вопросы:
s_
из аргументов в ctor, не конструируя s
совсем?s_
от s
Видя, как последний не используется?s
правильно построен, не может компилятор избежать конструкции hello
Видишь, как это не имеет другого применения? Или хотя бы отойти от этого?Я уверен, что в рамках того, что вы просите, можно сделать многое, при условии, Вы идете, чтобы связать время, и вы делаете bar
пусто и никогда не отменять новое где-либо.
Но тогда, как будто ваша программа пустая, она не имеет видимых эффектов.
Компилятору не разрешено перемещать конструкции s_
от s
по правилам абстрактной машины. Если вы хотите, чтобы он был построен, std::move
Это.
Ситуации, когда lvalue можно рассматривать как rvalue, ограничены и специфичны и включают return x;
заявления. Это не return x;
заявление.
Так твой код должен копия s
в s_
, Вполне возможно, что это также должно генерировать предупреждение как вопрос качества реализации.
Компилятору не разрешено выходить s
в s_
, Там были некоторые C ++ 20 предложения разрешить гораздо более агрессивные правила отбора.
Но по состоянию на данный момент исключение допускается только при условии «как будто», с предварительными значениями или с return x;
заявления. Как будто исключение действительно очень трудно доказать с помощью чего-то столь же сложного, как распределение, большинство компиляторов не пытаются. И это невозможно при генерации объектного файла, потому что кто-то может заменить глобальный распределитель.
Представьте себе глобальное переопределение распределителя, которое выводит количество выполненных распределений. Затем эти «никогда не используемые» объекты используются в том, что они должны распечатывать распределение, которое они делают.
Или глобальный распределитель, который вызывает exit
после 2 выделений. Полученный в результате абстрагирующий аппарат никогда не должен вызывать bar()
; если мы удалим ваши лишние объекты, программа не будет вести себя как стандартные мандаты.
Других решений пока нет …