Почему компиляторы C ++ не оптимизируют больше строковых конструкций в сценариях передачи по значению?

(Этот вопрос вдохновлен Беседа Николая Йосуттиса на 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Видя, как последний не используется?
  • Если любой из предыдущих ответов «да» — почему gcc и clang не делают этого?
  • Если s правильно построен, не может компилятор избежать конструкции helloВидишь, как это не имеет другого применения? Или хотя бы отойти от этого?

3

Решение

Я уверен, что в рамках того, что вы просите, можно сделать многое, при условии, Вы идете, чтобы связать время, и вы делаете bar пусто и никогда не отменять новое где-либо.

Но тогда, как будто ваша программа пустая, она не имеет видимых эффектов.

Компилятору не разрешено перемещать конструкции s_ от s по правилам абстрактной машины. Если вы хотите, чтобы он был построен, std::move Это.

Ситуации, когда lvalue можно рассматривать как rvalue, ограничены и специфичны и включают return x; заявления. Это не return x; заявление.

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

Компилятору не разрешено выходить s в s_, Там были некоторые предложения разрешить гораздо более агрессивные правила отбора.

Но по состоянию на данный момент исключение допускается только при условии «как будто», с предварительными значениями или с return x; заявления. Как будто исключение действительно очень трудно доказать с помощью чего-то столь же сложного, как распределение, большинство компиляторов не пытаются. И это невозможно при генерации объектного файла, потому что кто-то может заменить глобальный распределитель.

Представьте себе глобальное переопределение распределителя, которое выводит количество выполненных распределений. Затем эти «никогда не используемые» объекты используются в том, что они должны распечатывать распределение, которое они делают.

Или глобальный распределитель, который вызывает exit после 2 выделений. Полученный в результате абстрагирующий аппарат никогда не должен вызывать bar(); если мы удалим ваши лишние объекты, программа не будет вести себя как стандартные мандаты.

9

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]