Может ли использование С++ 11 "auto" ухудшить производительность или даже сломать код?
Этот вопрос является противоположностью существующего вопроса "Может ли использование "авто" С++ 11 повысить производительность?"
Один из ответов на этот вопрос показал, что использование auto
может иметь не только положительные, но и отрицательные эффекты.
Я считаю, что нам нужен отдельный вопрос, с ответами, сосредоточенными на этой стороне auto
.
Ответы
Ответ 1
С auto
нет конверсии в объявлении переменной + строка инициализации. Но если такое преобразование должно произойти в любом случае, это лучше произойдет один раз во время инициализации, чем несколько раз позже.
struct X {
...
};
struct Y {
operator X() const;
...
};
Y foo(); // maybe, originally its return type was X but later was changed to Y
void bar(const X& );
const auto x = foo(); // <-- conversion not happening here
//
for ( int i = 0; i < 100; ++i ) //
bar(x); // <-- silently rages here
Такое отложенное преобразование может нарушить код, когда auto
сочетается с ленивой оценкой (реальный мир пример 1, пример 2):
class Matrix { ... };
class MatrixExpression {
...
operator Matrix() const;
};
MatrixExpression operator+(const Matrix& a, const Matrix& b);
std::ostream& operator(std::ostream& out, const Matrix& m);
Matrix a = ...;
Matrix b = ...;
auto c = a + b; // evaluation of the matrix addition doesn't happen here
a[0][0] += 1;
std::cout << c; // matrix addition is evaluated here, using the new state of 'a'
Ответ 2
Заголовок вопроса задает "производительность".
auto
- это компоновка времени компиляции, которая позволяет себе заменять выведенным типом. Его использование не приводит к разным машинным инструкциям, чем если бы вы получили рукописное имя, которое оно выводило.
Проблема в том, что при управлении производительностью часто задаются спецификация типов и литье, а auto
может скрывать, что ведущие программисты говорят что-то другое, чем они предполагали:
std::vector<std::array<BigStruct, 10000>>& f();
auto va = f(); // copy
for (auto v: va) { // copies
// ...
}
если программист написал:
std::vector<std::array<BigStruct, 10000>> va = f();
или
for (std::array<BigStruct, 10000> v : va)
они бы признали, что они указали по-значение. Но std::array<BigStruct, 10000>
- это то, что выводит здесь auto
и в этих случаях это переводит в значение.
Люди бросают свою стражу и забывают, что auto
выводит тип, он не включает квалификацию ref.
auto& va = f(); // reference
for (auto& v : va) { // references
Здесь был эффект производительности, но это не вызвано auto
, это был побочный эффект (случайно), явно указывающий копию.
auto
не означает the same as this
означает an instance of this
.
auto va = f(); // an instance-of what f returns, thus a copy.
auto& va = f(); // a reference to an instance-of, thus by reference.
Ответ 3
Слепой поиск и замена всех объявлений типов с помощью Авто может быть головной болью, когда используется принудительная инициализация:
class Point
{
public:
Point (int x1, int y1) { x = x1; y = y1; }
private:
int x, y;
};
int main()
{
Point p{5, 6};
auto q{5, 6}; // Error. Uniform initialization is not REALLY uniform
}
Первое объявление переменной p корректно выводится как вызов конструктору, принимающему два целых числа. Но переменная auto q ищет конструктор, требующий std:: initializer_list <int
> и, следовательно, не скомпилируется.