• Как отключить определение функции через шаблоны?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Не так важно просто включить или выключить метод исходя из аргументов шаблона типа, как важно объяснить пользователю типа, почему там метод доступен, а тут - нет.

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

    И вот как это должно выглядеть в идеале.
    template< size_t ROWS, size_t COLUMNS >
    struct Matrix final
    {
    	template< size_t R = ROWS, size_t C = COLUMNS >
    	inline std::enable_if_t<R == C, int> GetDeterminant() const
    	{
    		return 0;
    	}
    };


    Важно гарантировать зависимость выражения SFINAE от параметров шаблона метода. Поэтому объявление шаблона выглядит именно так, а не как-то еще.

    Теперь, что будет если GetDeterminant не удалось вывести из шаблона? Будет ошибка трансляции, говорящая о том, что метод не найден. Это ничего не говорит пользователю. Это просто инструктирует транслятор остановить трансляцию. Пользователь, особенно если он не искушен знаниями о SFINAE, не сможет понять причину ошибки трансляции. Такая ситуация создаст риск излишней траты времени на дознание причин ошибки.
    Под конец выяснится что матрица просто не квадратная.
    Пользователю нужно объяснить причину отсутствия метода.

    Есть простой способ сделать это.
    template< size_t ROWS, size_t COLUMNS >
    struct Matrix final
    {
    	template< size_t R = ROWS, size_t C = COLUMNS >
    	inline std::enable_if_t<R != C, int> GetDeterminant() const = delete;
    
    	template< size_t R = ROWS, size_t C = COLUMNS >
    	inline std::enable_if_t<R == C, int> GetDeterminant() const
    	{
    		return 0;
    	}
    };


    Альтернативный путь вывода всегда должен быть. Его отсутствие или приведет к ошибке трансляции, или усложнит понимание для пользователя. В данном случае явно удаленный метод должен подтолкнуть пользователя к верной причине. Сообщение об ошибке будет говорить о том, что пользователь пытается использовать удаленный GetDeterminant.

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

    И делается это крайне просто.
    template< size_t ROWS, size_t COLUMNS >
    struct Matrix final
    {
    	inline int GetDeterminant() const
    	{
    		static_assert( ROWS == COLUMNS, "Matrix should be square to calculate the determinant." );
    		return 0;
    	}
    };


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

    hint000
    @hint000
    у админа три руки
    Читайте: https://ru.wikipedia.org/wiki/Деление_с_остатком
    Если после этого останутся вопросы - спрашивайте.

    о тегах к этому вопросу
    #дискретная-математика
    #высшая математика
    Какая к чёрту высшая математика? Это доступно для понимания в ~4-м классе средней школы.
    #криптография
    #математика
    Ответ написан
    5 комментариев
  • Рекурсия.По какой причине ответ всегда 0?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    Учитесь писать красиво. Вместо нагромождения вложенных if/else используйте ранний выход из функции.
    float power(float X, int N) {
        if (N == 0) {
            return 1;
        }
        if (N < 0) {
            return 1. / power(X, -N);
        }
        if (N % 2 == 0) {
            float b = power(X, N / 2);
            return b * b;
        }
        return X * power(X, N - 1);
    }
    Ответ написан
    Комментировать
  • Как проверить, является ли файл PE? (то есть, .exe он или нет)?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    не понимаю, чем в данном случае является szFile

    Судя по тому, как он используется, szFile -- указатель на буфер с содержимым файла. Почему такой странный выбор типа -- действительно непонятно, логично было бы использовать const void *.
    Ответ написан
    Комментировать
  • Как "забыть" переменную на c/c++?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега C
    Седой и строгий
    Переменные прекращают существование после завершения блока, в котором объявлены. Соответственно, используйте маленькие функции, объявляйте переменные как можно ближе к месту их использования и компилятор обо всём позаботится сам.
    Ответ написан
    Комментировать
  • Столкнулся с непонятным - откуда лишние 00000002?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Python
    Седой и строгий
    Ответ написан
    Комментировать
  • Задание функции с помощью массива точек, возможные решения, литература?

    vfreelancer
    @vfreelancer
    php
    гуглите интерполяционный многочлен
    Ответ написан
    Комментировать
  • Как правильно освобождать память?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Давай рассмотрим, для начала, строку под комментарием.

    ArrayArray[0] = *new Array<int>{10};
    Что тут происходит.
    ArrayArray[0] вернет ссылку на Array<int>.
    *new Array<int>{10} выделяет память в куче под Array<int>, вызывает инициализатор Array<int>::Array(int length), после чего делает разыменование получившегося указателя на Array<int>.
    После этого для подготовленной ссылки на Array<int> будет выполнен оператор копирования по умолчанию, функциональность которого просто скопирует поля из объекта справа в объект слева от присвоения.
    После этого результат new Array<int>{10} становится утекшим, т.к. указатель на него сразу становится потерян и освободить занимаемую им память становится невозможно.
    Именно поэтому с этой строчкой у тебя "всё отрабатывает нормально". И нет, всё у тебя не отрабатывает нормально потому что память утекла.

    Давай рассмотрим следующую строку.

    ArrayArray[0] = Array<int>{10};
    Что тут происходит.
    ArrayArray[0] вернет ссылку на Array<int>.
    Array<int>{10} инициализирует безымянный локальный объект на стеке, используя инициализатор Array<int>::Array(int length).
    После этого выполняется уже оператор перемещения по умолчанию, суть которого снова сводится к копированию полей из объекта справа в объект слева. После этого оба объекта ссылаются на одну и ту же выделенную память через поле T *m_data.
    Далее безымянный локальный объект уничтожается, освобождая недавно выделенную память. А ArrayArray[0] в этот момент начинает ссылаться на освобожденную память.

    Double deletion происходит тогда, когда ArrayArray в конце работы программы пытается удалить уже освобожденную память в ArrayArray[0].

    В твоем коде на лицо нарушение инварианта типа.
    Что нужно сделать.
    Тебе должно уже стать понятно что проблема в твоем коде связана с поведением операторов копирования и перемещения по умолчанию. Но проблема у тебя, на самом деле, значительно шире. Потому что завтра ты ведь точно захочешь еще и инициализацию копией провести или вернуть объект по значению из функции. В этом случае тебя тоже ждут проблемы.
    Решением твоих проблем будет соблюдение правила 3/5/0.
    Тебе нужно полноценно описать поведение объектов при копировании, перемещении, а так же при инициализации копией и инициализацией через перемещение.
    Ответ написан
    2 комментария
  • Как называется подобная задача?

    @Akela_wolf
    Extreme Programmer
    Экстраполяция.

    В данном случае данных маловато, все что можно сделать - это линейную экстраполяцию. То есть на 25% приходится (2000-30) = 1970. А значит при 50% это будет -1940. Но это на уровне
    extrapolating.png
    Ответ написан
    1 комментарий
  • Координаты (широта и долгота) в x и y?

    hint000
    @hint000
    у админа три руки
    Не выходит
    Вышло бы только в случае ориентированного по сторонам света прямоугольника (т.е. верх строго на север, низ строго на юг, право строго на восток, лево строго на запад).

    Если размер карты небольшой и кривизной земной поверхности можно пренебречь,
    ...то можно использовать аффинное преобразование.
    https://www.google.com/search?q=аффинные+преобразо...
    Также можете посмотреть более сложный метод.

    Я писал такой алгоритм, вернее код на C++, но это было примерно 20 лет назад. На досуге попробую поискать в залежах хлама на дисках, но пока ничего не обещаю, так что возможно, вы разберётесь и напишете быстрее, чем я найду. :)
    Ответ написан
    3 комментария
  • Как программисты хранят частоиспользуемые куски кода, чтобы каждый раз не вспоминать заново?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Программисты такое не забывают, чтобы где-то хранить. Забыть можно названия функций, коих 100500 в различных API. Но синтаксис и стандарты языка - это как бы алфавит. Вот вы часто забываете алфавит? Сможете сейчас его воспроизвести: "а", "б", "в"... ? А если другой язык: "a", "b", "c"... ? Также и (настоящие) программисты помнят подобное, как основы основ.

    Частоиспользуемый код оформляется в виде функций, и пропадает необходимость в копировании/переписывании. Многие такие простые функции входят в стандартную библиотеку, и названия таких функций практически не меняются от языка к языку. Поэтому даже если в языке не оказалось одной из таких функций, то она создаётся с заранее известным названием, которое программисты помнят хорошо. А если функция специфична для проекта, то таких функций не много, и их тоже легко помнить (на время работы с проектом).
    Ответ написан
    Комментировать
  • Как рендерят видео с множеством Мальдеброта?

    AgentSmith
    @AgentSmith
    Это мой правильный ответ на твой вопрос
    На шейдерах можно сделать
    https://www.shadertoy.com/view/4df3Rn
    Ответ написан
    Комментировать
  • Как вывести TCHAR в файл?

    @galaxy
    Что возвращает функция GetComputerName? (как вообще этот код скомпилировался, интересно?)
    Ответ написан
    5 комментариев
  • Падает программа при вызове PInvoke?

    Ну как минимум ты на стороне C++ вполне чётко написал, что calling convention - cdecl, а на C# почему-то написал stdcall.
    Попробуй вот так:
    [DllImport("C:\\CMakeProject2.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "test")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void Test();

    Ну и библиотеки лучше таки именно в библиотеки собирать, а не в екзешники, и ложить рядом с твоим приложением, а не где-то в глобальной папке.
    Ответ написан
    3 комментария
  • Как из c++ выполнить python?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала нужно решить, нет ли возможности оставить две программы отдельно, связав их через стандартный ввод/вывод, файлы или сокеты? Пусть одна запускает другую просто как дочерний процесс, это проще всего.

    Если это категорически должна быть одна программа, то нужно решить, кто будет "главным".

    Если главным будет Питон, который должен вызывать код на C/C++ (оформленный в виде DLL), то может пригодиться модель ctypes. Альтернативно, можно написать модуль интерпретатора.

    Если главным будет C++, который должен выполнять код на питоне, то тут уже нужно читать про embedding python.
    Ответ написан
    Комментировать
  • Как сделать дерево объектов из массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function createTreeFromArray(arr, key, parentKey) {
      const tree = Object.fromEntries(arr.map(n => [ n[key], { ...n } ]));
    
      return Object.fromEntries(Object.entries(tree).filter(([ , n ]) => {
        return !(tree[n[parentKey]] && ((tree[n[parentKey]].children ??= {})[n[key]] = n));
      }));
    }
    
    
    const tree = createTreeFromArray(arr, 'uid', 'parentUID');
    Ответ написан
    4 комментария
  • Как передать массив в функцию С++ ( пишет no matching function to call)?

    @res2001
    Developer, ex-admin
    Написал опус на вопрос, который вы удалили, хорошо сохранился в буфере обмена :-) а то было бы обидно.

    Ваш массив пожно просто привести к указателю, по старинке (int*)a или используя касты.

    Но у вас тут на самом деле 2 проблемы.
    Первая - обращение к элементам массива в average используя [i][j]. Вторая - VLA.
    По первой проблеме:
    В С/С++ оператор индексации (array[i]) выполняет следующее действие: *(array+i).
    Отсюда должно быть понятно, что раз array у вас это int*, то после array[i] вы получите int, от которого уже нельзя взять второй индекс, т.к. это просто 1 int, а не массив.

    Отсюда есть несколько выходов:
    1. дурацкий (самый долгий по исполнению и затратам памяти): использовать динамический массив массивов
    Выглядит примерно так:
    int **array = new int*[rowCount];
    for(int i=0; i < rowCount; ++i)
       array[i] = new int[colCount];

    Как видите array превратился в двойной указатель, теперь каждый элемент в первом измерении - это указатель на одномерный массив. Всего у вас получается rowCount + 1 выделений памяти. Не забудьте столько же раз вызвать оператор delete.
    В average теперь передавайте int** и у вас будет работать оператор [][], т.к. первая индексация уже будет возвращать int*.
    Не рекомендую использовать этотт способ.

    2. Вычислять индекс массива вручную (не использовать индексацию):
    *(array + i*colCount + j)
    Это такой хардкорный стиль. Но зато работает быстро и масштабируется на массивы любой размерности без особых проблем.

    3. использовать std::vector<std::vector<int>> - это то же самое, что и вариант 1, но закамуфлированный под вектор :-)

    Вторая проблема это VLA (Variable Length Array).
    Статические (автоматические) массивы в С++ вы можете определять, только константным размером (размером известным на этапе компиляции).
    У вас же размерность массива динамическая (вводится пользователем во время выполнения программы). Отсюда следует, что вы должны использовать динамические массивы, выделенные с помощью new.
    В стандарте С++ нет VLA. VLA есть только в Си и то начиная с С99.
    Ваша функция task3 компилируется, только потому что в gcc/clang по умолчанию включены расширения. В расширения входит так же и возможность использовать VLA. Если задать опциями более строгое соответствие стандарту С++, то функция не соберется.
    И кстати, например в микросовтовский компилятор VLA до сих пор не завезли.

    Но если уж у вас есть VLA, то вы можете преобразовывать указатель в VLA массив с помощью такой кучерявой конструкции:
    int (*array2)[colCount] = (int(*)[colCount]) array;

    В этом случае обращаться к элементам массива можно как обычно через двойную индексацию: array2[i][j]
    Когда-то делал тест на эту тему: https://ideone.com/4i6lRw
    Кстати, если в average сначала передать размерности, и последним параметром массив, то по идее массив можно сразу объявить двумерным, используя ранее переданные размерности:
    void average(int rowcount, int colcount, int aarray[rowCount][colCount])

    Это то же VLA.

    Вообще не рекомендую в С++ использовать VLA, т.к. программа становится не переносимой и зависимой от компилятора.
    Ответ написан
    4 комментария
  • Как передать массив в функцию С++ ( пишет no matching function to call)?

    Adamos
    @Adamos
    Используя Кресты, стоит использовать и их удобства.
    std::vector< std::vector< int > > a(rowCount, std::vector< int >(colCount, 0));
    void average (std::vector< std::vector< int > > &a){

    Так вы не нарветесь на промахи в индексах массива, например. Если умеючи.

    А в вашем коде int[][], конечно, не может самопроизвольно превратиться в int*
    Ответ написан
    2 комментария
  • Как разобраться с выводом valgrind?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    При попадании в блок:
    if (!result)
    память реально НЕ выделена и free от неё - это попытка освободить память по адресу NULL. Разумеется, это не будет работать.

    upd: Прочитал вопрос ещё раз внимательнее. Надо не забывать делать free в конечном месте использования этого result:

    result = get_next_line();<br>
    ...do_something_with_result...<br>
    free(result);


    Иначе да, при каждом вызове get_next_line будет выделяться новый блок на 2 байта.
    Ответ написан
    3 комментария