Может ли использование С++ 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 > и, следовательно, не скомпилируется.