Задать вопрос
  • Почему явная специализация невозможна?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Проблема вызвана использованием шаблона rewrite из шаблона search.

    Если вы перенесете специализацию шаблона rewrite вверх, до специализации search, то все скомпилируется. Или надо где-то выше первого использования шаблона rewrite задекларировать специализацию (что ваш закомментированный код и делает).

    Вызвана эта ошибка стандартом.
    Надо, чтобы специализация шаблона была задекларирована до любого использования:
    Specialization must be declared before the first use that would cause implicit instantiation, in every translation unit where such use occurs:
    Ответ написан
    1 комментарий
  • Уменьшается ли используемая память программы?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Не гарантированно, но в некоторых случаев компилятор действительно сможет переиспользовать место на стеке под переменную a для какой-то новой локальной переменной, когда a выйдет из зоны видимости. Но чаще это место просто будет пустым до конца функции и никакой экономии памяти вы не получите.

    Но вообще, делать так для экономии памяти никогда, категорически не рекомендуется. Код становится менее читаем а экономите вы на спичках. Это локальные переменные - они на стеке. Их много можно выделить только рекурсией или большими массивами (ну не объявите вы в коде миллион локальных переменных). В обоих случаях, если стека не хватает - надо или избавлятся от рекурсии/больших массивов изменением логики, или выносить их в кучу.

    Использование фигурных скобок для ограничения зоны видимости переменной действительно используется на практике, когда вам надо ограничить время жизни переменной и добиться вызова деструктора в определенное время. Так делают, например, когда захватывают мютекс в многопоточных программах - специальный класс-обертка в конструкторе его хватает, в деструкторе освобождает. И иногда не надо держать мютекс во всей функции, а только в определенном месте. Допустим, дальше идут долгие вычисления, не требующие мютекса. Тут логично мютекс освободить. Но это должно встречаться редко. Если вам в функции надо несколько раз такое проворачивать, то надо ее отрефакторить и разбить на части.
    Ответ написан
    Комментировать
  • Уменьшается ли используемая память программы?


    Стоит ли на практике такое делать?

    В некоторых случаях это действительно полезно.


    Если да, то как лучше оформлять это в коде для читабельности?

    По возможности - лучше не применять.


    то переменная очистится и память требуемая программе уменьшется

    Нет, не очистится. Она же на стеке - под нё уже заранее заготовлено место.

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

    Ну и в случае с одним int это будет байта четыре - что вообще очень смешной объём памяти по сравнению с 16мб*, которые выделяются на стек потока.

    * Цифра не точная, может зависеть от ОС и её настроек.
    Ответ написан
    Комментировать
  • Уменьшается ли используемая память программы?

    bingo347
    @bingo347
    Crazy on performance...
    Во-первых, размер стека фиксирован, стек выделяется в момент запуска потока.
    Во-вторых, компилятор и сам достаточно умный, чтобы переиспользовать стек под разные переменные использование которых не пересекается.
    В-третьих, экономия на спичках, а читаемость ухудшается.

    P.S. такую штуку действительно иногда используют, но ради того чтоб вызвать деструктор в нужной точке кода.
    Ответ написан
    Комментировать
  • Можно ли при вызове функции указать в него тип данных?

    @dima20155
    you don't choose c++. It chooses you
    Полагаю, что вам удобно будет использовать здесь шаблоны, если я правильно понял вопрос.
    Например:
    template <typename T>
    auto search (std::string str) {
        // T - data type
        T res;
        // do something
        return res;
    }
    
    int main () {
        auto a = search<int>("a");
        auto b = search<std::string>("a");
    }
    Ответ написан
    5 комментариев
  • Вопрос по оформлению кода C++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Каждый вопрос здесь подразумевает довольно большой объем информации при обосновании ответов. В общем смысле весь вопрос сводится к выбору одного из стилей написания кода.
    Обычно стиль кода закреплен стандартом языка, но в стандарте C++ такого нет. Поэтому стиль кода в C++ является предметом выбора каждого. Опять же, обычно для C++ стиль кодирования выбирают наобум, просто потому что понравилось так или потому что в этом стиле пишет любимая компания (GCS - хороший пример выбора на эмоциях и яркий пример очень плохого стиля для C++). Но обоснование своего выбора является очень важным.
    В C++ Core Guidelines есть отдельная секция с описанием стиля кодирования стандартной библиотеки.
    И тем не менее.

    1) Как называть переменные:

    Зависит от того, как будут называться функции и константы.
    И вот почему
    Книжки читают быстро, а код - еще быстрее. При беглом чтении всегда нужно уметь разделять переменные, константы и функции. C++ итак сложный, а если все будет написано в одной манере, то код на C++ будет только еще сложнее.
    Моей рекомендацией будет переменные и локальные константы писать в lower_cast_snake_style, а глобальные константы, макроопределения и элементы нестрогих перечислений писать в UPPER_CAST_SNAKE_STYLE.
    Таким образом достигается единообразие. Стиль змейки во всех своих видах сразу отходит под описание данных, создавая акцент для читателя. Таким образом данные будут читаться легче.
    Свои типы, имена элементов строгих перечислений, имена пространств и имена функций с методами, при этом, стоит писать в UpperCamelCase. Почему все эти и только в одном стиле. А потому что они и концептуально связаны, и разделены настолько, что не перемешиваются при чтении.
    Все составные типы формируют свои пространства имен для вложенных объявлений. Поэтому строгое перечисление, структура, класс или пространство имен разумно называть в едином стиле.
    Функции являются точками входа в подпрограмму, их стилистически неверно было бы писать, например, в lowerCamelCase. Первая заглавная буква много значит при чтении, она является акцентом для читателя.


    2) Что лучше присваивать булевым переменным:

    Литералы 0 и 1 имеют тип int. Если тип переменной - bool, то с какой стати справа от типа должны присутствовать значения с типом int?
    Следует использовать только литералы с типом bool: true и false.
    И вот почему
    При написании кода самым важным является не отражение алгоритма или формальное соответствие стандарту, а именно не вызывать вопросов у читателя. Нужно всегда понимать, что у читателя свой контекст, читатель решает свою задачу, здесь у тебя в коде он только для сбора информации. Его не должны сбивать с толку никакие изыски в написанном коде. Когда читатель видит слева тип bool, а справа значение с типом int, у него появляются вопросы, закрадывается подозрение в достоверности прочитанного, он выпадает из своего контекста. Это - очень плохо.


    3) Как лучше называть переменые итераторы во вложенных циклах:

    Абсолютно каждое имя должно отвечать на вопрос: "Зачем ты тут существуешь?"
    Могут ли однобуквенные имена ответить на этот вопрос внятно через всего одну свою букву? Нет.
    Имя - это смысл. Имя - это причина существования. Имя - это цель использования.
    И вот почему
    Чтение кода вынуждает читателя создавать и поддерживать некоторый контекст читаемого кода. Чем сложнее читателю дается поддержка такого контекста, тем менее понятен читаемый код и тем больше времени уйдет на его изучение. Если же в результате читателя выкинет из контекста решаемой им задачи, то это будет совсем плохо и виноват в этом будет именно плохо написанный код.
    Написанное в коде имя создает отметку в контексте для читателя. Чем более это имя понятно и отвечает общему изложению кода, тем легче читателю дается поддержка контекста читаемого кода.
    Существует масса концепций именования, море семантических пар имен, гора ярких и кучи общих имен. Важным остается только одно - переменная должна своим именем говорить о том, что она хранит, а функция - что делает. Тип должен в своем имени раскрывать природу существования своих объектов.
    Имя должно быть обязательно конкретным. Data, Interface, Iterator - это общие имена, которые не несут никакой конкретики. Общие имена допускаются только в абстрактном коде, т.е. в коде интерфейсов, шаблонов, макросов. Между именем вызываемой функции и именем переменной, принимающей результат вызываемой функции должна быть семантическая связь. И разрыв этой связи допускается только при переходе от общего имени к конкретному. Например так: auto hosts = local_network.GetIterator();. И ведь тут с полпинка все становится понятно, даже думать не надо.


    4) Очень локальный вопрос стоит ли писать else, если ниже нет другого кода ниже:

    Ветвление всегда подразумевает ровно один прыжок или продолжение исполнения кода. Иногда ветвление подразумевает два прыжка: или прыжок в начало альтернативной ветви, или прыжок из конца основной ветви за пределы кода ветвления. Оптимизатор сам выбирает лучший вариант реализации ветвления, более выгодную основную ветвь и от писателя в этом процессе мало что зависит. Но для читателя ветвление и циклы всегда подразумевают очень большое усложнение кода. else стоит писать только тогда, когда без него иначе невозможно.
    И вот почему
    При чтении кода важно чтобы код был понятен читателю. Когда в коде появляется ветвление, читатель вынужден раздвоить контекст читаемого кода для себя. Это всегда сложно. Если читатель видит только одну ветвь в ветвлении, второй контекст читателю дастся легче. Если читатель видит что у ветвления есть две ветви, они будет вынужден напрячься чтобы поддержать сразу два контекста в параллели. И если в конце окажется что вторая ветвь ветвления - это лихо замаскированная линейная часть остатка кода до конца подпрограммы, у читателя снова появятся большие вопросы к целям такого изложения кода.


    6) Писать ли пробел между стандартными функциями и скобками:

    Пробелы нужны для разделения связанных цепочек символов - слов. Код - это запись рассуждений автора о том, что должна делать программа. Код должен читаться как рассказ, в котором слова правильно разделены между собой и правильно расставлены смысловые акценты.
    И вот почему
    Пробелы нужны чтобы отделить одно от другого. С какой целью? Наверное с целью обратить внимание читателя на то, что пробелами отделено. Пробелы сами не являются акцентами, но позволяют акцентировать внимание читателя, в то время как любые другие символы только забирают на себя внимание потому что читателю надо понять смысл присутствия символов в месте их присутствия.
    a==5 - никаких акцентов, ничего не видно. Даже с подсветкой синтаксиса 5 и == читаются плохо и практически неотличимы от a=5 при беглом чтении. В такие моменты у читателя в контекст вносится ошибка или, как минимум, неопределенность ошибки. Но основная цель писателя кода - это написать понятный для чтения код. Поэтому через пробелы надо акцентировать внимание читателя именно на символе эквивалентности - a == 5, позволяя ему правильно прочитать написанное при беглом чтении.
    if(a == 5){ - в этом коде видно только акцент на знаке эквивалентности, но не на выражении условия. if (a == 5) { - уже лучше, но скобки требуют от читателя понять природу их нахождения, что это именно условие, а также вчитаться в левый и правый аргументы условия. if( a == 5 ){ - здесь для читателя акцент поставлен именно на всем условии, теряется только знак начала области видимости - {. И именно поэтому египетские скобки - это плохо. Область видимости должна начинаться на своей строке, потому что для нее нужно создать максимально заметный акцент.
    for (int a = 0; a < 10; a++) { - тут акценты созданы, но не так, чтобы читатель легко прочитал тип счетчика или операцию шага. for( int a = 0; a < 10; a++ ) - а вот тут внимание читателя акцентируется именно на выражении счетчика. И читателю уже не надо выискивать глазами условия, инициализацию и шаг. Это все выделено пробелами и подано для самого комфортного чтения.


    7) Тот же вопрос только про функции, что я сам написал:

    С этого момента тебе должно стать понятно, на чем именно нужно делать акценты чтобы не выводить читателя из себя. Главное - это при написании кода всегда помнить, что возможно читать его будет натуральный маньяк-психопат, который точно знает где ты живешь. И ты точно не хочешь разгневать его своим кодом. :)
    И вот почему
    Код всегда пишется для читателя. Не для транслятора, не для чего-то еще. Транслятору важно только формальное соответствие кода стандарту. Читателю важно понять логику кода, а для этого код надо читать и разбираться в его логических связях. Поэтому, когда пишешь код, всегда нужно думать о том, как его будут читать, не будет ли вопросов к конкретным строчкам, понятны ли имена и отражает ли написанное вложенную в этот код логику.
    Ответ написан
    2 комментария
  • Вопрос по оформлению кода C++?

    mayton2019
    @mayton2019
    Bigdata Engineer
    Вопрос по оформлению кода C++?

    Если делаешь себе пет-проекты или фрилансишь - то безразлично. Делай как читабельно лично
    для тебя. Из best-practices. Старайся уменьшать количество вложенных for/if/else. Для
    уменьшения - делай декомпозицию на функции. И добавляй guard-expressions с выходом
    в return сразу когда какое-то условие выполняется. Часто это уменьшает число вложенной логики.
    break - как аналог guard только для циклов.

    Если ты зашел в корпорацию - то первое что ты увидишь - это тонны легаси кода и корпоративные
    требования (локализованные под проект) о том как надо писать. Конвенции. Соглашения.
    И просто договорняки в команде. Например у нас было такое что в проекте мы после пары
    багов решили писать switch/case с комменарием в секции default ВСЕГДА в том случае
    когда default не содержал логики. Тоесть программист как-бы подтверждал что все
    кейсы учтены и мы ничего не потеряли в проверках.
    switch(weekday) {
      case SAT: 
         onSaturday();
      default:
         // Nothing to do!
    }

    Фигурные скобочки и пробелы обычно подсвечивают корпоративные плагины в IDE с настройками.
    Еще вариант что стиль чекается во время процедур CD/CI и выдает ошибки или предупреждения
    о нарушении стиля. Тоесть сильно упарываться сейчас этим вопросом не стоит. Когда тебя
    прижмет - привычка писать правильно появится очень быстро. За считанные недели.

    Следующий пример цикла - просто неудачен.
    for (int a = 0; a < 10; a++) {
        if (a == 5) {
            break;
        }
        std::cout << "Не то значение" << std::endl;
    }

    Здесь можно доказать что он на самом деле сводится к циклу от 0 до 4. И вообще без проверки внутри.

    Видишь очень важно правильно подбирать учебные примеры.
    Ответ написан
    1 комментарий
  • Вопрос по оформлению кода C++?

    @rPman
    Coding style должен быть таким, как определено в вашей команде
    Если это твоя команда - решение принимать нужно после общения со своей командой, выбрав тот стиль, наименее конфликтующий с их стилями.

    Если это твой проект - делай так как тебе удобно, я серьезно.
    Можно в качестве основы почитать про мировую практику, причем смотри, с какой целью это делается.
    Ответ написан
    2 комментария
  • Вопрос по оформлению кода C++?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    ГЛОБАЛЬНО:

    Тратить время, внимание и силы на педантичный подсчет пробелов и расстановку скобок - контрпродуктивно. Достаточно настроить автоформат перед коммитом и сосредоточиться на корректности кода.

    1) Равноправно, но первое веет PEP8
    2) Однозначно true/false. Возможность прямого каста чисел в логику является тяжлым наследием C и ведет к интересным потенциальным багам.
    3) Обычно i, j, k потому что олдовые программисты учили в первую очередь математику, а потом уже алфавит. А если вложений больше трех, пора браться за рефакторинг.
    4) Нет, не стоит. Цель как раз в том, чтобы выпрыгнуть из блока как можно раньше, а не вкладывать блоки друг в друга. Опять же, если вложений больше трех - в рефакторинг
    5) Вы пропустили этот пункт, тренируйте внимание
    6 - 7) Вкусовщина, лично я не пишу эти пробелы. Но я и не использую египетские скобки для циклов (а вы - используете)
    Ответ написан
    5 комментариев
  • Вопрос по оформлению кода C++?

    @dima20155
    you don't choose c++. It chooses you
    Я бы полистал уже готовые рекомендации по стилю. Из них можно подчеркнуть интересные вещи, которые могут сказаться даже на качестве самого ПО и потенциальных ошибках.
    Вот пример.
    https://google.github.io/styleguide/cppguide.html
    Ответ написан
    1 комментарий
  • Почему Config::search у меня возвращает мусор?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Проблема вот в этой строчке:
    return str.c_str();

    Тут вы возвращаете указатель на внутренние данные у str. Но при выходе из функуции str уничтожается - это же локальная переменная. В итоге у вас получается висячий указатель (указатель на память, которой вы уже не владеете). Эту память какая-то другая часть вашей программы переиспользует и там остается что-то не ваше, выглядещее для вас, как мусор.

    Вообще, это undefined behavior - доступ к висячему указателю. Программа вполне может и аварийно завершится.

    Для решения этой проблемы возвращайте std::string. Или выделяйте char* вручную, через new[] (только не забудьте указатель потом удалить в вызывающем коде). Но лучше, конечно, возвращать string и не мучатся с ручным управлением указателями.
    Ответ написан