Инициализация переменных функции

Поиграв с некоторыми примерами, я нашел следующую конструкцию:

void Show(int nav = 0) // -1 сверху, 1 снизу

и мне стало интересно, какой эффект имеет инициализация параметров хендовера в объявлениях функций, я никогда сознательно не замечал такой конструкции.

Итак, я сделал небольшую демонстрацию:

#include <M5Stack.h> //необходимо для моей платы
  int a;
  int b;
  int c;

int addFunction(int x = 3, int y = 3){
  int result;
  result = x + y;
  return result;
}

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

  // тестовый пример 1
  a = 1;
  b = 2;
  c = addFunction(a, b); 
  Serial.println("testcase 1:");
  Serial.println((String)a + " + " + b + " = " + c);

   // тестовый пример 2
  a = 1;
  b = 2;
  c = addFunction(); 
  Serial.println("testcase 2:");
  Serial.println((String)a + " + " + b + " = " + c);

   // тестовый пример 3
  a = 1;
  b = 2;
  c = addFunction(a); 
  Serial.println("testcase 3:");
  Serial.println((String)a + " + " + b + " = " + c);
}

void loop() {
}

Вывод:

Итак, я узнал:

  • если вы передаете параметры, инициализация игнорируется, если вы вызываете функцию без передачи параметров, функции работают со значениями инициализации.

  • если вы передаете только один параметр, он будет присвоен первому в объявлении .

Вопрос:

Существует ли синтаксис, позволяющий вызывать функцию только со вторым параметром, чтобы в качестве первого параметра использовалось инициализированное значение объявления? В этом примере с a = 1, b = 2 вы передаете только b, а ожидаемый результат будет 5.

, 👍1

Обсуждение

Должен ли всегда быть один параметр для второго параметра (добавление перегруженной функции) или вы хотите выбрать, для какого параметра?, @Jot

Это просто общий вопрос для понимания синтаксиса. Вот мне был бы интересен ответ как это сделать вообще, а значит еще и с большим количеством параметров и возможностью выбора любого параметра в зависимости от цели (m из n), @RJPlog

Перегруженная функция — это один из вариантов приема различного количества параметров. Указатель может иметь значение nullptr (или NULL), и в коде вы можете проверить наличие nullptr. Должны быть и другие варианты., @Jot

@Jot Tanks за подсказку. Я посмотрел на это, думаю, это из-за того, что я не работаю, поскольку x и y имеют один и тот же тип. Я проверю, заработает ли он, возможно, с разными типами., @RJPlog


2 ответа


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

4

Это (к сожалению) вопрос не по Arduino. На самом деле речь идет о синтаксисе C++, который является довольно сложным языком.

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

f(int x = 0, int y = 1, int z = 2)

f(1,2,3) binds x = 1, y = 2, z = 3
f(1,2) equals f(1,2,2) binds x = 1, y = 2, z = 2
f(1) equals f(1,1,2) binds x = 1, y = 1, z = 2
f() equals f(0,1,2) binds x = 0, y = 1, z = 2

В C++ существует синтаксис инициализации структуры (также известный как агрегатная инициализация), который позволяет присваивать имена значениям полей структуры. Этот тип именования не допускается для вызовов функций.

Прочитайте https://en.cppreference.com/w/cpp/language/default_arguments , https://en.cppreference.com/w/cpp/language/aggregate_initialization и https://en.cppreference.com/w/cpp/utility/functional/bind для получения более подробной информации. И хорошая книга по C++.

Существуют языки, которые допускают аргументы по умолчанию и именованные параметры, например C#, Python, Ruby и Smalltalk. См. https://en.wikipedia.org/wiki/Named_parameter.

Удачи!

(Обновление)

Ниже приведена переписанная версия @VE7JRO Добавить класс функции с использованием оператора приведения для выполнения оценки:

class Add
{
public:
  Add(int x, int y) : m_x(x), m_y(y) {}
  operator int() { return m_x + m_y; }
  void x(int x) { m_x = x; }
  void y(int y) { m_y = y; }
  int print(Print& outp) {
    int res;
    res = outp.print(F("Add(x="));
    res += outp.print(m_x);
    res += outp.print(F(",y="));
    res += outp.print(m_y);
    res += outp.print(F(")"));
    return res;
  }
private:
  int m_x, m_y;
};

Add func(1,2);

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

  func.print(Serial);
  Serial.print(F("="));
  Serial.println(func);

  func.x(12);
  func.print(Serial);
  Serial.print(F("="));
  Serial.println(func);

  func.y(13);
  func.print(Serial);
  Serial.print(F("="));
  Serial.println(func);
}

void loop() {
}
,

Хорошее объяснение и материал для дальнейшего расследования, также спасибо за расширение @VE7JRO, оно добавляет еще несколько интересных для меня деталей. (после того, как я проверил, что вы используете другую скорость передачи данных ;-)), @RJPlog

Пропустил разницу в скорости передачи данных. Это изменено, чтобы избежать путаницы, отладки и т. д. между эскизами., @Mikael Patel

BW: Если данные члена станут общедоступными, будет разрешен синтаксис func.x = 12 (после удаления префикса m_)., @Mikael Patel


1

Если ваша функция находится внутри класса, вы можете установить любую переменную, обе переменные или использовать значения по умолчанию.

class Add{

    int a, b;

  public:

    Add(): a(1), b(2){}

    int AddTwoIntegers(){
      return a + b;
    }

    void SetInt1(int int1){
      a = int1;
    }

    void SetInt2(int int2){
      b = int2;
    }

};

Add Test1;

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

  Serial.print("Test default values = ");
  Serial.println(Test1.AddTwoIntegers());

  Test1.SetInt2(4);
  Serial.print("Set b = 4 then add to a = ");
  Serial.println(Test1.AddTwoIntegers());
}

void loop(){}
,

понял, спасибо за пример, очень помог мне в понимании, пока пробовал, @RJPlog