Пользователь пока ничего не рассказал о себе

Наибольший вклад в теги

Все теги (7)

Лучшие ответы пользователя

Все ответы (18)
  • Как убрать ошибки взаимодействия между классами?

    @code_panik
    Если структуру классов не нужно менять, то можно сделать как в рабочем примере.
    Перед Window_mgr достаточно объявить (forward declare) class Screen. Просто нужно иметь ввиду, что в файле класса Window_mgr это неполный тип (incomplete type).
    И во friend объявлении не хватает Window_mgr::ScreenIndex.
    Ответ написан
    1 комментарий
  • Как превратить void() в void (**)()?

    @code_panik
    UPD: Похоже, проблема в библиотеке
    https://github.com/espressif/arduino-esp32/issues/7675
    Исправляющий комит
    https://github.com/espressif/arduino-esp32/commit/...

    Довольно странное требование для api.
    Если вы пишите класс-обертку над этой функцией, то она принимает просто функцию, а не указатель на указатель.
    В C++ функция определяется своим адресом, и правила передачи функции в функцию похожи на правила передачи обычного массива.
    Похоже, проблема именно в объявлении вашего register_callback, которое можно реализовать как в этом примере
    #include <iostream>
    using namespace std;
    
    using esp_spp_cb_event_t = int;
    using esp_spp_cb_param_t = void;
    
    void btCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
        cout << "btCallback" << endl;
    }
    
    class MyBt {        
    public:
        typedef void (*callback_type)(esp_spp_cb_event_t, esp_spp_cb_param_t *);    
    
        void register_callback(callback_type cb) {
            cb(0, nullptr);
        }
    };
    
    int main()
    {    
        MyBt bt;
        bt.register_callback(btCallback);
        return 0;
    }
    Ответ написан
    5 комментариев
  • Каковы правила конвертации указателя на массив неопределенной длины в указатель на массив определенной длины?

    @code_panik
    В выражении p = p2 мы выполняем неявное преобразование встроенных типов, для которых в стандарте не описаны правила преобразования. То есть такое преобразование не гарантировано, но может быть реализовано компилятором, например
    #include <type_traits>
    using namespace std;
    
    int main() {
        static_assert(std::is_convertible<int(*)[2], int(*)[]>::value, "Non-convertible");
        return 0;
    }

    компилируется в gcc 12.2, в clang 15.0 - ошибка.

    Существует старое не принятое предложение о закреплении такого преобразования стандартом.
    Ответ написан
    1 комментарий
  • Разобраться с моим кодом, можно?

    @code_panik
    Ошибка в попытке применить вызов функции к переменной типа int, а не функции.
    Возникает, когда в bot.register_next_step_handler(message, x1) передается не функция x1, а значение типа int, которое записывается в global x1 после первого запуска функции x1.
    Начните с переименования функций или переменных.
    Ответ написан
    5 комментариев
  • Почему msvc оптимизирует конструкторы несмотря на флаги?

    @code_panik
    Разберемся, в каком порядке выводятся сообщения на экран во втором случае gnu++11.
    Сначала в main вызываются два конструктора по умолчанию: для imnotdumb1 и временного объекта dumb_array();.
    Далее в main вызывается метод dumb_array & operator=( dumb_array temp), в котором temp инициализируется посредством метода dumb_array (dumb_array&& other), который перед выводом на экран сообщения operator=( dumb_array tmp) вызывает ещё один конструктор по умолчанию.
    Итого 3 конструктора по умолчанию, один перемещения, один оператор присваивания.

    MSVC вызывает первые два конструктора по умолчанию и применяет move elision к инициализации temp.
    То есть весь пример сводится к такому коду
    #include <iostream>
    using namespace std;
    
    struct Foo {
    	Foo() { cout << "default ctor\n"; }
    	Foo(Foo&& arg) { cout << "move ctor\n"; }
    };
    
    void foo(Foo arg) {
    	cout << "foo\n";
    }
    
    int main () {    
    	foo(Foo());
    	return 0;
    }


    В c++ существуют обязательные (mandatory) и необязательные случаи copy/move elision, https://en.cppreference.com/w/cpp/language/copy_elision.
    Применение правила из примера
    In the initialization of an object, when the source object is a nameless temporary and is of the same class type (ignoring cv-qualification) as the target object.

    до c++17 было необязательным к реализации, на усмотрение разработчиков компилятора.
    В Microsoft считают такое правило обязательным вне зависимости от флагов компиляции,
    Mandatory copy/move elision in Visual Studio

    The C++ standard requires copy or move elision when the returned value is initialized as part of the return statement (such as when a function with return type Foo returns return Foo()). The Microsoft Visual C++ compiler always performs copy and move elision for return statements where it is required to do so, regardless of the flags passed to the compiler. This behavior is unchanged.


    До c++17 требовалось, чтобы соответствующий copy/move ctor в случае copy elision всё равно был доступен (cppreference, там же):
    This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed
    Ответ написан
    1 комментарий