Как стать автором
Обновить

Комментарии 76

Как оказалось, не только.

При неаккуратном использовании float и double можно и без auto выстрелить себе в ногу.

Начиная с C++ 11, надо писать не enum, а enum class.

enum class byte : unsigned char ... Видимо из-за авто выведения типов сейчас и в enum указывается тип данных.

Я говорю про свои типы данных. Хотите строгой типизации - пишите enum class.

Почему-то ответы не туда попадают :(

Новый баг Хабра. Ответы появляются в первом уровне ветки, но стрелки указывают на предыдущий комментарий.

Грядут улучшения?

Я не из администрации. Они ещё предыдущие баги не починили, поэтому не буду пока грузить их новыми :D

НЛО прилетело и опубликовало эту надпись здесь

У меня к auto по большому счету одна претензия - нужно смотреть код, чтобы узнать возвращаемый результат функции или метода. И если файл для беглого просмотра открыт не в полноценной IDE с подсказками, а в простом редакторе....

Да, есть такое дело.

Зачем работать не в полноценной IDE с подсказками? Что может заставить хоть сколько-нибудь серьезный проект (состоящий не из одного файла main.cpp) разрабатывать в простом редакторе?

GitLab, например, не выводит подсказки о типе переменной во время ревью, и таких примеров много.

Есть куча очевидных кейсов, типа:


var newbornKittenExecutor = executorFactory.CreateForNewbornKitten();

Здесь замена var на имя типа только увеличит визуальный шум.

Да, конечно, я честно говоря сам уже не припомню, когда явно указывал тип переменной вместо var.

НЛО прилетело и опубликовало эту надпись здесь

В целом проблема в перегруженности C++ и багажа обратной соместимости.
В том же C#, например, нет никакой проблемы с использованием var.

Что-то вы написали какие-то очень несовременные банальности, которые и без авто в языке были проблемами. Авто внёс свои, но ни одну из них вы не затронули. А вся эта игра с числами была всегда, например

float half = 1/2;

Вас же не удивляет, что здесь будет ноль?

Ну а противиться фиче через 12 лет после её внедрения, когда уже со всех сторон разобраны хорошие и плохие её применения, это как-то странно. У меня есть много кода, который без авто либо не написать вообще (сложные шаблоны), либо он будет выглядеть настолько ужасно, что читать его невозможно.

Я сам не люблю, когда авто используют слишком часто, или вообще рекоомендуют использовать везде. Слишком падает читаемость, IDE не всегда спасает. Но совсем отрицать - перебор. Моё личное требование - тип должен быть

  • либо написан в этой же строке кода, условно auto o = new Object();

  • либо быть сложным для написания, но очевидным (auto iter = vec.begin()),

  • либо быть очень сложным или невозможным для написания (сложные шаблонные выводы)

Имеет смысл рассматривать вопросы читаемости, не всем очевидные правила отбросывания ссылок, работу с промежуточными типами вроде vector<bool>::reference, а не вот эти наличия буковки f.

Авто внёс свои, но ни одну из них вы не затронули.

Не вопрос: затроньте и осветите, будет интересно узнать.

Просто напомню, что классический пример "сложного шаблонного вывода", где auto уместен - это произведение прямоугольных матриц произвольных размеров

Интересно, как вот такое без auto написать?

auto Sum(auto a, auto b) {
  return a + b;
}

И ведь сюда не только числа можно в качестве аргументов передавать.

Когда шаблоны только появились, вот так писали, с явным указанием типа:

template <typename T> T add(T a, T b) {
    return a + b;
}

int i = add<int>(2, 3);
double f = add<double>(5.0, 7.0);

И кажется, чуть позже, но еще до auto, компилятор сам научился выводить тип, так что можно было просто писать add(1, 2).

У меня а и b разных типов могут быть. Ну понятно, что можно через шаблоны написать. Но длиннее получится.

Так в шаблоне и несколько типов может быть - template <typename T1, typename T2> . Ясное дело, что без auto длиннее будет, его и придумали, чтобы всё это не писать.

НЛО прилетело и опубликовало эту надпись здесь
template <typename T1, typename T2>
decltype(std::declval<T1>() + std::declval<T2>()) add( T1 a, T2 b )

Не очень красиво, правда.

"Когда шаблоны только появились", так написать было нельзя, а когда появился std::declval, то появился и синтаксис с trailing return type:

template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b)
{
    return a + b;
}

Тут, конечно, есть слово auto, но скорее для красоты :)

Когда шаблоны только появились

Да, действительно плохо было.
Ну тогда можно было вот так поизвращаться:


template <typename T1, typename T2>
struct add_result { };

template <typename T>
struct add_result<T, T> { typedef T result_type; };

template <>
struct add_result<int, float> { typedef float result_type; };

template <typename T1, typename T2>
typename add_result<T1, T2>::result_type add( T1 a, T2 b )
{
    return a + b;
}

Для какой-нибудь библиотечки, работающей с изображениями, можно быть объявить так правила работы с основными типам.

ИМХО, писать вот так, лишь бы не использовать auto - это почти и есть "стрелять себе в ногу". Как минимум - "есть кактусы" :-)

Тяжело читать фрагменты кода в статье из-за ужасного форматирования.

Будто автор бережёт ресурс своего пробела.

У меня вот сломался пробел. Огромная проблема, на самом деле. Пришлось менять привычку и переназначать пробел на правый-альт.

НЛО прилетело и опубликовало эту надпись здесь

Нам приходится поддерживать совместимость с сильно устаревшими ОС, поэтому даже С++11 не получается использовать.
Суровый отечественный легаси.

имхо, auto не совместим с короткими названиями [x,y,z...]. Если названия несут смысл то и знать тип не обязательно.

Ну например у лямбд вообще невозможно ручками записать "нативный" тип. Да, их можно привести к std::function, но это уже не совсем то-же самое...

А если перед телом лямбды поставить -> int например?

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

зачем

писать

код

через

строчку?

А отсутствия пробелов не смутили, значит?

Причём в начале они были.

В школе в Borland C++ 3.1 я и сам писал без пробелов, потому что на экране 80x25 кода влезало очень мало. А если еще и не разворачивать cmd на полный экран, то шрифт был очень широким, поэтому арифметические выражения с пробелами выглядели глупо.

Автор невероятно старательно пишет самые кривые примеры кода известные человечеству, не получает никаких проблем и потом говорит "какой ужас"

Судя по результатам голосования, многие читатели нашли материал небесполезным.
Значит он был написан не зря.

Приколов в с++ слишком много, недавно например узнал, что тренарный оператор является и rvalue и lvalue. Попробуйте туда еще auto прикрутить, может еще какие загадки появятся.

(true ? a : b) = 1; //a=1
(false ? a : b) = 1; //b=1

нет он не является и lvalue и rvalue, у вас неправильное определение rvalue как "сущность которой можно присваивать" - это неправильное определение

вместо тысячи слов можно просто скомпилировать и проверить


    int a=0;
    int b=0;
     
    (true ? a : b) = 1;
    std::cout << "a=" << a << "b=" << b << "\n";
    (false ? a : b) = 1;
    std::cout << "a=" << a << "b=" << b << "\n";

И что же интересно я тут должен узнать? Что тетрарный оператор ВНЕЗАПНО возвращает общий тип его двух аргументов, в данном случае int&?

НЛО прилетело и опубликовало эту надпись здесь
float f(float x) {

      auto n=0.1f;

      return x * n; 

}

float g(float x) { 

    auto n=0.1;

    return x * n;

}

Поразительно: если вручную расставить типы литералов, то компилятор выбирает выбранные типы.

Я вам сейчас ещё пример подкину

auto x = -1ull;
std::cout << x << std::endl;

Невероятно, но тут тоже будет не -1. Это просто разгром.

Похоже, вы просто не до конца поняли с чем воюете.
Обдумайте приведенный пример и прочтите присобаченную статью.

Я из другого лагеря и мне авто не хватает. Хочу авто не просто везде где сейчас можно, а вообще везде - типы параметров функций (да я знаю можно получить что хочу через лямбды, но без дедусит зис с++23 это не то, мне рекурсия часто нужна). Хочу авто в декларации мемберов класса, а тип пусть из инициализатора выводится, соглашусь с автором в одном П-последовательность с++ это нечто ни кем не виденное, статики в классах значит можно с авто, а мемберы низя, почему?

Типы они для компиляторов и они очень нужны, но людей от них надо держать подальше. Для людей есть имена/идентификаторы, вот они должны быть такими что бы вам пофигу было на тип. Конечно, всегда есть исключения вот для них и оставить явные типы.

Автор не убедил, все проблемы поднятые в статье закрываются юнит тестами, и не важно авто у вас там или явные типы. Пишу авто везде где работает уже лет 10, полет отличный.

По поводу сравнения между собой чисел с плавающей точкой - если не хотите стрелять себе в ногу, это лучше делать через приведение их к целым числам с нужной точностью, и с округлением вручную, если нужно. Например, если нужно сравнивать между собой частоты кадров, это обычно делается с точностью до одной сотой секунды (для NTSC частота кадров 29.97):

auto IsEqualFrameRate(double a, double b) {
  return int(a * 100.0 + 0.5) == int (b * 100.0 + 0.5);
}

В принципе, можно "обнаглеть" и вместо double тоже написать auto. Тогда можно будет подсовывать в a и b хоть double, хоть float. Если в a и b действительно частоты кадров (которые, например, берутся из параметров видеофайлов), ни к каким проблемам это не приведет.

А использовать

std::abs(a - b) <= kEpsilon

уже не модно?
(С умножением чем-то эффективнее (нет вызова простой функции?)?)

Вариант с умножением, имхо, более универсальный - сработает на любой платформе, компиляторе (если не использовать auto, то и старом) и т.д.

std::numeric_limits<T>::epsilon есть в C++ вроде как ещё до C++11

Я вот смотрю описание - std::abs разве не целочисленный?

fabs конечно же должно быть)

Там в конце написано:

See also
abs(float)

, которое ведёт на fabs.
А в fabs :

float abs( float num );
double abs( double num );
long double abs( long double num );
(until C++23)

Про fabs я в курсе. Но он выдает тоже число с плавающей точкой. С которым по-прежнему надо обращаться аккуратно.

НЛО прилетело и опубликовало эту надпись здесь

Есть способ выстрелить себе в ногу (точнее, в производительность), стараясь избежать auto. Пример отсюда:

std::map<int, int> _map;
for (const std::pair<int, int>& c : _map) {
  // ...
}

В результате вместо того, чтобы пройтись по ссылкам на элементы контейнера, цикл на каждом шаге будет создавать копию элемента. А всё потому, что надо писать внутри цикла const std::pair<const int, int>&, а еще лучше const auto&

https://godbolt.org/z/7eTG5de9T

выглядит сомнительно - gcc и clang генерят одинаковый код для обоих вариантов цикла. чяднт?

Ну так вместо первого int попробуйте большую структуру подсунуть. Пример был условный.

А можно по-стариковски:

for(std::map<int, int>::iterator it=_map.begin();it != _map.end();++it){
  //it->first ...
  //it->second ...
}

По-стариковски - это ещё и свой класс контейнера должен быть ;-)

long y=1;
for (auto x = 100000001.0f; x <= 100000010.0f; ++x) {
++y;
}

Что тут произойдет, знаете?

Как в бородатом анекдоте:
— Вовочка, а что можно получить, если подушку разрезать?
— Звездюлей от дедушки можно получить!

Так и тут. Итерировать дробными числами это действительно фееричный способ отстрелить себе ноги. Только какой смысл вообще рассматривать такие кейсы? Тот кто такое пишет у себя в коде либо точно знает что он делает, либо вообще не знает основ.

А ведь бывают случаи, когда приходится именно дробными числами итерировать. Сталкивался с таким в алгоритме фильтрации КИХ-фильтром (FIR). Слава богу, там было не <=, а просто <

Это как раз тот случай когда

тот кто такое пишет у себя в коде точно знает что он делает

Если вы хаотично и бездумно смешиваете знаковые и беззнаковые, разной точности и разрядности, - то вам это auto или отказ от него - вообще не помеха для того, чтобы накосячить.

Ну вот написали auto i = arr.size()-2, а если бы написали там size_t i = arr.size()-2, вам что, стало бы легче?
Или если бы вы сделали проверку вида if (arr.size()-2 < 0) вместо якобы эквивалентной if (arr.size() < 2) ?
Говнокодить - так уж говнокодить до конца.
А long long - это вообще что было? Правильный тип в данном случае - это ptrdiff_t. Который может быть, а может и не быть long long.

Опять же, если вы серьёзно следите за погрешностями в плавающей арифметике, то уж как-нибудь соблюдёте чистоту рук, и у вас в каждом конкретном месте auto будет резолвиться в тот тип, который вы ждёте и который вам очевиден. Например, вы договорились везде использовать float, чисто для детерминизма, чтобы программа грешила одним и тем же способом. (И всё равно она будет варьировать - если от оптимизации зависит порядок вычислений, а вы это пустили на самотёк).

А чтобы вы меньше лажали, форсируйте варнинги компилятора до ерроров.

Этак, наверное, можно доказать опасность вообще любого слова в языке. Если один человек сделал, то другой завсегда сломать может.

C++ со своими фичами - это когда два разных программиста могут написать код, делающий одно и то же, и при этом совершенно не понять код друг друга.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории