Использование Serial в качестве аргумента булевой функции

Следующая программа компилируется в среде Arduino IDE.

void doSomething(bool) {}

void setup() {      
  doSomething(Serial);
}

void loop() {}

Но, как и ожидалось, в этом случае этого нет:

class MyClass {};

void doSomething(bool) {}

void setup() {   
  MyClass myClass;
  doSomething(myClass);
}

void loop() {}

Компилятор возвращает ошибку невозможно преобразовать 'MyClass' в 'bool' для аргумента '1' в 'void doSomething(bool)'


Почему возможно скомпилировать первый вариант? Разве Serial не является обычным экземпляром класса (HardwareSerial)? Имеет ли смысл интерпретировать Serial как тип bool?

, 👍-1

Обсуждение

Вы не смотрели документацию, да? Поэтому я и проголосовал против., @the busybee

@thebusybee Не обращайте внимания на минусы, но я не согласен с вашими доводами (и с несколько высокомерным риторическим вопросом). Я считаю, что мир Arduino (включая сайт StackExchange) — отличное место для изучения основ программирования, а умение ориентироваться в документации и находить решение таких вопросов, как этот, — это навык, который нужно освоить, поскольку для новичка это неочевидно., @noearchimede

Что ж, как и в любом другом деле, включая непрограммистское ПО, первым делом всегда обращаемся к **предоставленной документации**, а не к случайному поисковику, сайту StackExchange или даже ChatGPT. Однако я каждый день замечаю, что этот навык угасает. Всего наилучшего и удачи!, @the busybee


2 ответа


Лучший ответ:

5

HardwareSerial определяет оператор bool(), что позволяет использовать его в логическом контексте.


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

В конкретном случае HardwareSerial это означает, что последовательный порт готов: «Это вернет false только в том случае, если запрос на последовательное соединение USB CDC Leonardo будет сделан до того, как оно будет готово». См. документацию <code>if(Serial)</code>.

,

И каково назначение оператора bool()?, @noearchimede

Классы определяют, можно ли преобразовать их экземпляры в тип bool и как это значение, а также что оно означает. В конкретном случае HardwareSerial это означает готовность последовательного порта: «Этот код вернёт false только при запросе последовательного соединения USB CDC Леонардо до его готовности». — https://www.arduino.cc/en/Serial/IfSerial, @deltab


0

Добавляя к ответу, вы можете проделывать подобные чудеса с помощью своих классов/структур, как показано ниже (ну, ответ уже содержит ссылку на способ, но мне просто захотелось добавить его сюда).


Выполнение пользовательской функции преобразования (как это называется в Cppreference здесь)

Чтобы определить свой индивидуальный состав, просто создайте такого участника

operator <desired-type-here> ()

Пример: предположим, у нас есть класс Integer, который мы хотим использовать как логическое значение в операторе if, например, if(x):

class Integer {
public:
    Integer(int x) : val{x} {}

    int val;
};

void setup() {
    Serial.begin(9600);
}

// смотрите здесь
void loop() {
    Integer x(42);
    if (x) { // Эта строка, очевидно, выдаст ошибку
        Serial.println("I swear I forgot what 42 referenced, darn it.");
    }
    else {
        Serial.println("0 is a very powerful number");
    }

}

Теперь давайте реализуем наше пользовательское приведение в функции следующим образом:

class Integer {
public:
    Integer(int x) : val{x} {}

    // здесь произойдет волшебство
    // ПРИМЕЧАНИЕ: «const» здесь необязателен. Вы можете указать его, если хотите.
    // разрешить использование с "const Integer"
    operator bool() const { 
        return (val != 0);
    }

    int val;
};

void setup() {
    Serial.begin(9600);
}

// смотрите здесь
void loop() {
    Integer x(42);
    if (x) { // теперь это работает и выведет истинное утверждение.
        Serial.println("I swear I forgot what 42 referenced, darn it.");
    }
    else {
        Serial.println("0 is a very powerful number");
    }

    bool yes = x; // это утверждение теперь также верно, поскольку "x" приводится к bool, а результат присваивается "yes"
}

Дополнительно: Вы также можете сделать так, чтобы для использования функции преобразования требовалось явное приведение типа, добавив explicit к объявлению функции следующим образом:

explicit operator <desired-type>()

Допустим, вы хотите, чтобы ваш класс Integer приводился к типу float, но при этом вы хотите явно указать момент приведения. Поэтому мы изменим наш класс следующим образом:

class Integer {
public:
    Integer(int x) : val{x} {}

    // Мы... прокомментируем это здесь, потому что компилятор попытается привести
    // Преобразовать целое число в число с плавающей точкой, сначала приведя его к типу bool, а затем преобразовав этот тип bool
    // к числу с плавающей точкой. Вы также можете указать это явно, чтобы этого не произошло (но
    // помните, что вам нужно будет явно приводить типы)
    /*operator bool() const { 
        return (val != 0);
    }*/

    // Давайте сделаем здесь больше магии
    explicit operator float() const {
        return static_cast<float>(val); // "(float)val" также работает, но чем уродливее синтаксис приведения, тем легче его найти в коде, что, по-видимому, считается лучшим вариантом.
    }

    int val;
};

И теперь мы можем сделать это в нашем коде:

void loop() {
    Integer x(42);

    float y = x; // Компилятор оценит ваши прошлые действия по этому вопросу.
    float z = static_cast<float>(x); // Компилятор с радостью это примет.

}
,