Перегрузка функций

KindMan
Offline
Зарегистрирован: 19.12.2018

Сразу к делу:

class MyClass {
public:
  void Func(boolean p1 = false);
  void Func(uint8_t p1, boolean p2 = false);
};

void MyClass::Func(boolean p1) {}
void MyClass::Func(uint8_t p1, boolean p2) {}


MyClass Test;

void setup() {
  
  Test.Func(true);
  Test.Func(1);
  
}

void loop() {}

Ясное дело в 15-16 имею ошибку ambiguous
Переделал так

enum MyBool {False = 0, True};

class MyClass {
public:
  void Func(MyBool p1 = False);
  void Func(uint8_t p1, MyBool p2 = False);
};

void MyClass::Func(MyBool p1) {}
void MyClass::Func(uint8_t p1, MyBool p2) {}


MyClass Test;

void setup() {
  
  Test.Func(True);
  Test.Func(1);
  
}

void loop() {}

И собственно вопрос - Как сделать правильно? Без лишних приемов профи, а лаконично и надежно в моей ситуации?
 

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

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

Feofan
Offline
Зарегистрирован: 28.05.2017

Подпишусь

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

А что говорит это:

class MyClass {
public:
  void Func(boolean p1 = false);
  void Func(uint8_t p1, boolean p2 = false);
};

void MyClass::Func(boolean p1) {}
void MyClass::Func(uint8_t p1, boolean p2) {}


MyClass Test;

void setup() {
  
  Test.Func(true);
  Test.Func((uint8_t)1);
  
}

void loop() {}

Просто предположение...

KindMan
Offline
Зарегистрирован: 19.12.2018

BOOM пишет:

Просто предположение...

Приведение к типу, конечно работает, просто мне визуально так не нравиться

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

А пожертвовать дефолтным значением p2 никак? 

sadman41
Offline
Зарегистрирован: 19.10.2016

Визуально не нравится - суйте в функцию переменную соотв. типа.

Ибо:

....
#define TRUE      1
....
#define true      TRUE
....

 

b707
Offline
Зарегистрирован: 26.05.2017

KindMan пишет:

Ясное дело в 15-16 имею ошибку ambiguous

ну оно же и вправду амбигоус, вы же сами понимаете

 

Цитата:
И собственно вопрос - Как сделать правильно?

Каждый вариант функции должен иметь УНИКАЛЬНЫЙ набор параметров. Если наборы получаются похожими, можно ввести холостой парметр, который нужен только для различения вариантов функций:

 void Func(boolean p1 = false);
  void Func(uint8_t dummy,  uint8_t p1, boolean p2 = false);

 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

либо убери default значения параметров.  Совсем, оба.

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

DetSimen пишет:

либо убери default значения параметров.  Совсем, оба.

Насколько я понимаю, для четкого определения достаточно убрать дефолт второго аргумента, а дефолт единственного параметра первой функции не помешает?

b707
Offline
Зарегистрирован: 26.05.2017

Rumata пишет:

Насколько я понимаю, для четкого определения достаточно убрать дефолт второго аргумента

конечно.

Подойдет любое изменение заголовков, которое сделает два заголовка уникальными

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Rumata пишет:

Насколько я понимаю, для четкого определения достаточно убрать дефолт второго аргумента, а дефолт единственного параметра первой функции не помешает?

Да

KindMan
Offline
Зарегистрирован: 19.12.2018

Спасибо за отклик, в целом я так и думал. Видел с шаблонами заморочки, но для меня слишком заумно. Мне тогда проще создать функцию с другим именем, для редко используемого не дэфолтного состояния.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, понятно, что вызов 

Test.Func(1);

не соответствует точно ни одной из перечисленных выше функций, т.к. 1 имеет тип int. Компилятор смотрит куда можно преобразовать и обнаруживает, что можно хоть в uint8_t, хоть в bool. Вот он и не может выбрать.

Решений миллион. Одно Вы привели сами. Другое (указать точный тип первого параметра) привел BOOM в #3. Все решения хорошие и правильные. Можно привести ещё десяток. Например, сделать первый параметр не uint8_t, а int - тогда совпадение типа будет точным и это будет более высокий приоритет, чем у преобразования в bool. Но это решение хуже, чем то, что привёл BOOM, т.к. будет дополнительный расход ресурсов на передачу двухбайтового значения, а оно там не нужно.

Если Вы эстет и параноидальный экономист ресурсов, то Вы можете поставить вопрос: а можно ли сделать, чтобы не писать (uint8_t) перед 1, но при этом не расходовать лишние ресурсы, как было с параметром типа int? Можно и так. Достаточно добавить ещё один inline метод вот так:

class MyClass {
public:
  void Func(boolean p1 = false);
  void Func(uint8_t p1, boolean p2 = false);
  inline void Func(int p1, boolean p2 = false) { Func((uint8_t)p1, p2); }
};

void MyClass::Func(boolean p1) {}
void MyClass::Func(uint8_t p1, boolean p2) {}

MyClass Test;

void setup() {
  Test.Func(true);
  Test.Func(1);
}

void loop() {}

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

Можно ещё десяток вариантов привести - все хорошие. Какой использовать - хозяин-барин, иногда есть доп. соображения по задаче. Главное, общее правило соблюдать - список параметров должен однозначно позволять выбрать правильную функцию.

Если хотите подробнее, читайте стандарт, там этому посвящено ажно более 30 страниц.

KindMan
Offline
Зарегистрирован: 19.12.2018

Спасибо, Евгений Петрович! Всегда с удовольствием читаю ваши образовательные посты!