Задать вопрос
Ответы пользователя по тегу C++
  • Как с помощью шаблонов проверить, что два числа равны?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Отвечаю на вопрос из комментариев. Можно сделать так:

    template <int a, int b>
    class A {
        public:
        template<bool tmp = true>
        typename std::enable_if<a==b && tmp, int>::type F() {
           return 1;   
        }
    };
    
    ...
    
    A<1,1> a;
    std::cout << a.F();  // OK.
    A<2,100> b;
    std::cout << b.F(); // Ошибка A<2,100> не имеет метода F().


    Мне сложно объяснить, чтобы было понятно, почему работает именно это, но я попробую.

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

    Далее, хотелось бы что бы просто работало вот это:
    template<>
     typename std::enable_if<a==b, int>::type F()

    Тут все понятно, enable_if не имеет type, если a неравно b. Шаблон никак не инстанциировать и метод не должен был бы генерироваться. Но это было бы слишком просто.

    Вместо этого, надо там завести какой-то вообще ненужный как бы параметр шаблона tmp, и обязательно использовать его в enable_if. Это потому что если в этом шаблоне не будет никак использоваться параметр шаблона, то SFINAE не срабатывает, и вылезает ошибка компиляции.
    Ответ написан
    Комментировать
  • Как дополнить двумерную матрицу нулями по краям?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Сначала каждую строку resize до размера size()+w0+w1. Потом сдвигайте элементы на w0 позиций вправо (цикл надо гнать с конца). Потом заполните пустые места нулями.

    То же самое в самом векторе векторов. Только вместо чисел надо сдвигать вектора на h0 позиций. На пустые места надо будет записывать std::vector(n+w0+w1, 0);

    Edit: А вообще, тут и добавлять в массив ничего не надо. Вы нужный вам ответ можете нулями прямо во время вывода добить.
    Ответ написан
    Комментировать
  • Как разбить объявление класса и его реализацию?

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

    Чтобы это исправить нужно или засунуть все в h файл, или в cpp файле прописать специализации шаблона со всеми типами, которые где-либо еще используются, вот так:
    using A<int>;
    using A<double>;
    // etc


    Далее, как именно и зачем вы хотите этот внутренний класс скрыть?
    Иногда вам может быть достаточно сделать член этого типа private и все. Факт того, что кто-то где-то сможет завести переменную этого типа обычно не является проблемой. Для списка уж точно. Если же это проблема и внутренний класс является friend внешнего и что-то такое делает, что нарушает инкапсуляцию (например при учнитожении, что-то меняет в родительском классе), то просто сделав вложенный класс private, вы ничего не добъетесь. Переменные этого типа все-равно можно будет использовать и к ним обращатся (иногда через хаки). Обычно у внутренних классов просто делают приватным конструктор и нужные методы и прописывают внешний класс как friend. Тогда вы в самом классе сможете переменные этого типа создавать и использовать, но никто снаружи ничего с ним сделать не может.

    И кстати, в вашем случае вам совсем нет смысла делать вложенный класс шаблоном. Он уже в шаблоне же, просто используйте внутри тип T.
    Ответ написан
    Комментировать
  • Как передать list в функцию c++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В параметрах функции:
    list ItemsList
    но в другом месте:
    list <int> ItemsList

    list - это шаблон или как?
    Ответ написан
  • Проверить первые 2 байта?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Окройте файл в ifstream в бинарном режиме и читайте 2 байта через read.

    Или используйте fread. Читайте 2 байта в буфер длинной 2.
    Ответ написан
    Комментировать
  • Рекурсия.По какой причине ответ всегда 0?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Аккуратно расставьте отступы. У вас там логика if else напутана, и программа делает совсем не то, что бы вам хотелось. Кроме того, посмотрите внимательно на warning-и от компилятора (он точно скажет, что power иногда не возвращает никаких значений, хотя вам кажется, что return есть во всех ветках if else). Это как раз потому, что у вас скобки не так расставлены и else относится совсем не к тому if, как вы хотели бы.
    Ответ написан
    Комментировать
  • Зачем в стандартной библиотеке вначале класса объявлены псевдонимы для параметров шаблона?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это делается по нескольким причинам.

    Во-первых, чтобы можно было к параметру шаблона обращатся в других шаблонах. Вот, вы привели пример с _Сontainer в каком-то итераторе. Можно представить что есть какой-то алгоритм (функция), который принимает итератор и ему там надо будет как-то использовать тип контейнера (или временную переменную завести, или тип возвращаемого значения задать или как-то проверить, что тип контейнера обладает какими-то свойствами).

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

    Во-вторых, иногда это делается и для удобства. Ибо в этих шаблонах параметры сами могут быть адски нагроможденными шаблонами.
    Ответ написан
    Комментировать
  • Как сравнить 2 класса с разными шаблонными параметрами?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    А не проще ли так?
    template <const std::size_t other_columns>
     Matrix<rows, other_columns, T> MultiplyOnMatrix(const Matrix<columns, other_columns, T>& matrix)
    Ответ написан
    1 комментарий
  • Как перегрузить оператор += для двух массивов?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    У вас нет проблем с перегрузкой. У вас там проблема с логикой программы. Для начала объясните себе, что хранит в себе класс Array (зачем там 2 массива a и b?), что должен делать опреатор += для двух экземпляров Array?
    Ответ написан
    Комментировать
  • Почему можно обратиться к объекту через указатель со значением nullptr?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Метод класса под капотом - это функция, котрой помимо прописанных параметров передается указатель на объект. В вашем случае передается nullptr. Но сама функция не обращается к объекту, да у него и членов-то с данными никаких нет. Поэтому все более менее работает. Но это UB и могло и не повезти. Из-за оптимизаций компилятор мог бы что-то сломать.

    P.s. сказанное выше не относится к виртуальным функциям. Там указатель на функцию храниться в vtable в экземпляре класса и вызов через nullptr скорее всего упадет.
    Ответ написан
    Комментировать
  • Почему в СТЕКЕ разрешается выделять достаточно мало памяти?

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

    Во-первых, потому что стек программе выделяется один раз сразу, а вот динамическую память все программы разделяют между собой. И пока одна программа ест гигабайт - вторая программа может жрать 10, а когда вторая завершится - первая может съесть эти 10 гигабайт. Со стеком так не получится, он должен быть непрерывен по адресам. И тогда каждой программе придется всегда выделять себе максимально возможный объем памяти, даже если он может не понадобится. Именно поэтому программы обычно не выделяют много стека.

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

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

    Edit: да, если вам нужно больше стека, можно сделать так, чтобы программа получала больше стека. В С++ это делается параметром линкера.
    Ответ написан
    Комментировать
  • Как реализовать copy on write class?

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

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

    Счетчик должен храниться где-то вместе с буфером и является частью умного указателя.

    Можно использовать std::shared_ptr - там уже этот счетчик реализован (use_count).
    Ответ написан
    Комментировать
  • Почему в массив записываются числа не и указанного диапазона?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    *p = (rand( )%100 - 50);
    for (int i = 1; i < k; i++) {
      *(p + 1) = -i * * (p + i - 1);
    }


    Можете вообще объяснить, что этот код делает? Зачем он это делает? Что вы хотели, чтобы он делал?

    Помимо этого, вместо *(p+X) можно писать p[X]. Так понятнее. У вас тут явно не к месту арифметика указателей приплетена.

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

    Поскольку массив у вас локальный, то он нулями не инициализируется. Поэтому там изначально случайный мусор. Поэтому все, кроме первого элемента так и будет случайным мусором.
    Ответ написан
  • Как правильно освобождать память?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В деструкторе удаляйте, только если память не nullptr или длина не 0.
    Ответ написан
    Комментировать
  • Из за чего появляется ошибка ошибка ld returned 1 exit status?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Выше написанно undefined reference to 'processingArray(int*, int)'


    Ну вот. У вас функция processingArray объявлена, но нигде не реализована. Тело функции где? Поскольку вы ее объявили, компилятор это безобразие проглатил и понадеялся, что эта функция реализована в другом файле и на этапе линковки она найдется. Но линкер ее не нашел, потому что ее нигде и нет.

    Вы, видимо, не все из учебника переписали. Там после функции main скорее всего идет реализация функции processingArray.
    Ответ написан
    7 комментариев
  • Как решить проблему Segmention Fault?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Segmentation fault - это ошибка работы с памятью. Или выход за границы массива, или что-то не так с указателями.

    Я подозреваю, что в файле у вас не 50 строк (мне лень считать). Вот вы и вылезаете за границу массива.

    Ну или там какие-то лишние переводы строк, пробелы имеются. поэтому fin.eof() не выполняется после считывания последней строки. Потом вы пытаетесь что-то еще прочитать, но получаете назад пустую строку. Это бы не было проблемой, но вы потом пытаетесь это все запихать в 51-ый элемент массива.

    Советую вместо define N и статического массива использовать std::vector. В цикле считывания файла делайте push_back нового объекта в вектор (а еще лучше, emplace_back для экономии лишнего копирования).

    При выводе берите size() у вашего вектора.

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    А зачем это делать? Чем вас 8 хедеров не устраивают? Если уж так хочется сэкономить строчки в коде пользователей библиотеки, то можно воспользоваться рекурсивностью препроцессора и сделать хедер с 8 инклудами. Копипастить код точно не надо.
    Ответ написан
    1 комментарий
  • В чём ошибка реализации алгоритма Прима?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    if ((matrix[i][j] < minValue[j]) && (isPartOfOutputTree[j] == false))


    Вот тут ошибка. Ведь i - это номер итерации,а совсем не только что включенная в дерево вершина, относительно которой надо сделать релаксацию.
    Ответ написан
  • Как удалить первый элемент однонаправленного списка в с++?

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

    Еще отдельная проблема с удалением первого элемента, потому что там менятся должна не ссылка next у предыдущего элемента, а ссылка на начало списка. Эту проблему можно решить по разному. Например, можно вместо указателя head держать один элемент списка без значения. Тогда указатель на начало списка будет менятся также как и любой другой. Фактически, в любом списке всегда будет один фиктивный элемент. Этот способ также предподчителен для работы с двусвязными списками. Тогда список как бы замыкается в кольцо и не надо разбирать отдельно ни случай первого, ни случай последнего элемента. Мне больше нравится другой метод - можно помнить не предыдущий элемент, а указатель-на-указатель, который надо будет поменять.

    node** prev_link = &head;
    node* cur = head;
    for  (node* cur = head; cur != nullptr; cur = cur->next) {
      if (cur->val == to_del) {
        *prev_link = cur->next;
        delete cur;
        break;
      }
      prev_link = &cur->next;
    }
    Ответ написан
    1 комментарий
  • Как исправить ошибку "Нарушение прав доступа при записи по адресу"?

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

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

    Можно во время отладки нажать на "продолжить" и тогда дебаггер остановится именно на той инструкции, которая вызвала ошибку. Дальше уже можно смотреть, в какую переменную вы там пишите и откуда она взялась.

    Падает оно потому, что нельзя string читать и писать в файл вот так, просто интенрпретируя память объекта как char*. Потому что string содержит в себе указатели на динамически выделенную память.

    Поэтому, когда вы его (в составе AutoBase) пишите в файл а потом читаете, вы получаете указатель на адрес, который был жив вместе со старым экземпляром класса. Однако, после удаления этого старого экземпляра, этот адрес уже вам не принадлежит.

    Вы можете так писать в файл только структуры из простых типов, и то не всегда (там всякое выравнивание может сыграть с вами злую шутку). Надо писать собственные методы сериализации и десериализации. Строку можно сохранять, например, как длину и потом столько байт, сколько нужно. Тогда при считывании вы сначала чистаете сколько-то байт размера, а потом нужное количество байт в саму строку.
    Ответ написан
    1 комментарий