Re[3]: Внутренние функции класса
От: Videoman Россия https://hts.tv/
Дата: 17.01.19 14:49
Оценка: +3
Здравствуйте, PavelCH, Вы писали:

PCH>А почему не вариант? Может просто объявить private функцию и не порождать монстров? Стоит ли игра свеч?

А зачем мне объявлять функцию в классе, если она полностью не зависит от класса, а нужно, только, выполнить полезное действие в реализации?
Какие преимущества:
— Не засоряется пространств имен класса. Private хоть и защищает от вызова снаружи, но участвует в перегрузке. Можно использовать короткие названия, понятные из контекста, во всех наследниках и не париться.
— Вызывая такую функцию, я полностью уверен что она не меняет инвариант класса. Т.е. на лицо меньшая связность, да еще и компилятор это проверит.
— Реализация функции может требовать дополнительный инклюды, которые не нужны в публичном интерфейсе класса, что будет засорять пространство имен и, возможно, приводить к перекомпиляции.

PCH>Стоит ли игра свеч?

О каких свечах идет речь? Если что, писанины меньше, перекомпиляций тоже.
Re[4]: Внутренние функции класса
От: PavelCH  
Дата: 17.01.19 15:11
Оценка:
V>Какие преимущества:
V>- Не засоряется пространств имен класса. Private хоть и защищает от вызова снаружи, но участвует в перегрузке. Можно использовать короткие названия, понятные из контекста, во всех наследниках и не париться.
Не совсем Вас понял. Можете привести пример? Насколько я помню там работает так:
struct A {
    int get() { return 1; }
};
struct B : A {
    int get(int n) { return n; }
};

int main() {
    B b;
    return b.get(); // Тут ошибка компиляции
}

V>- Вызывая такую функцию, я полностью уверен что она не меняет инвариант класса. Т.е. на лицо меньшая связность, да еще и компилятор это проверит.
Ну она же не доступна наружу. Возможно это не надо?
V>- Реализация функции может требовать дополнительный инклюды, которые не нужны в публичном интерфейсе класса, что будет засорять пространство имен и, возможно, приводить к перекомпиляции.
Те функции, которые требуют дополнительные инклюды, я выделяю в отдельные cpp файлы. В итоге это снижает время компиляции, если я ничего не меняю эта часть функционала не перекомпилируется.
PCH>>Стоит ли игра свеч?
V>О каких свечах идет речь? Если что, писанины меньше, перекомпиляций тоже.
Не уверен что метод 3 занимает меньше писанины. Насчет перекомпиляция тоже вопрос. Вообще я стараюсь делать служебные функции static и невидимые в h — но они конечно не имеют доступа к private членам класса. Если он нужен и только в этом случае применяю private функции класса.
Нехай щастить
Re[5]: Внутренние функции класса
От: Videoman Россия https://hts.tv/
Дата: 17.01.19 16:10
Оценка:
Здравствуйте, PavelCH, Вы писали:

PCH>Не совсем Вас понял. Можете привести пример? Насколько я помню там работает так:

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

PCH>Ну она же не доступна наружу. Возможно это не надо?

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

PCH>Те функции, которые требуют дополнительные инклюды, я выделяю в отдельные cpp файлы. В итоге это снижает время компиляции, если я ничего не меняю эта часть функционала не перекомпилируется.

Т.е. если вдруг вам понадобилась одна функция, то вы создаёте для нее отдельный файл .cpp, а если, в процессе рефакторинга, она перестала быть нужна, вы всё обратно объединяете? Вы думаете это проще?

PCH>Не уверен что метод 3 занимает меньше писанины. Насчет перекомпиляция тоже вопрос. Вообще я стараюсь делать служебные функции static и невидимые в h — но они конечно не имеют доступа к private членам класса. Если он нужен и только в этом случае применяю private функции класса.

Слушайте, но бывают конечно классы, легкие снаружи, но сложные внутри, которые в реализации используют кучу функций и даже других классов. И вот в этом случае разделение на дополнительные файлы .h и.cpp оправдано, но применять такую стратегию постоянно — слишком, на мой взгляд.
Re[5]: Внутренние функции класса
От: rg45 СССР  
Дата: 17.01.19 17:13
Оценка: +2
Здравствуйте, PavelCH, Вы писали:

PCH>Не совсем Вас понял. Можете привести пример? Насколько я помню там работает так:

PCH>Ну она же не доступна наружу. Возможно это не надо?
PCH>Те функции, которые требуют дополнительные инклюды, я выделяю в отдельные cpp файлы. В итоге это снижает время компиляции, если я ничего не меняю эта часть функционала не перекомпилируется.
PCH>>>Стоит ли игра свеч?
PCH>Не уверен что метод 3 занимает меньше писанины. Насчет перекомпиляция тоже вопрос. Вообще я стараюсь делать служебные функции static и невидимые в h — но они конечно не имеют доступа к

Вот в этой книжке (см. рекомендацию №44) сформулирован несложный критерий, когда следует делать функцию членом класса. Звучит этот критерий примерно так: "делайте функию членом класса в том случае, когда у вас нет другого выхода". А во всех остальных случаях, соответственно, не делайте. И можно вести бесконечно долгие дискуссии на эту тему, приводя бесчисленные доводы "за" и "против", но убедиться в истинности этого правила можно только на личном опыте.
--
Отредактировано 17.01.2019 20:03 rg45 . Предыдущая версия .
Re: Внутренние функции класса
От: B0FEE664  
Дата: 18.01.19 08:52
Оценка:
Здравствуйте, Igore, Вы писали:

I>Добрый вечер, возник тут у меня вопрос, а как вы оформляете внутренние(служебные, чистые) функции класса.


I>1) Просто делаем private:

I>2) Делаем class_inl.h
I>3) В cpp делаем namespace {
I>4) Увидел сейчас static inline inner()

5) Лямбда.
И каждый день — без права на ошибку...
Re[2]: Внутренние функции класса
От: rg45 СССР  
Дата: 18.01.19 10:21
Оценка:
Здравствуйте, B0FEE664, Вы писали:

I>>Добрый вечер, возник тут у меня вопрос, а как вы оформляете внутренние(служебные, чистые) функции класса.


I>>1) Просто делаем private:

I>>2) Делаем class_inl.h
I>>3) В cpp делаем namespace {
I>>4) Увидел сейчас static inline inner()

BFE>5) Лямбда.


Даже не знаю, уместно ли вспоминать о лямбдах в данном контесте. Все-таки, лямбда — это элемент парадигмы, позволяющей оперировать функциями как данными, но никак не средство оформления. И по отношению к лямбдам возникают почти все те же вопросы, что и по отношению к функциям: какая область видимости, какое связывание, член или не член...
--
Отредактировано 18.01.2019 10:39 rg45 . Предыдущая версия . Еще …
Отредактировано 18.01.2019 10:38 rg45 . Предыдущая версия .
Отредактировано 18.01.2019 10:37 rg45 . Предыдущая версия .
Отредактировано 18.01.2019 10:35 rg45 . Предыдущая версия .
Отредактировано 18.01.2019 10:32 rg45 . Предыдущая версия .
Отредактировано 18.01.2019 10:31 rg45 . Предыдущая версия .
Отредактировано 18.01.2019 10:22 rg45 . Предыдущая версия .
Re[3]: Внутренние функции класса
От: B0FEE664  
Дата: 18.01.19 10:51
Оценка:
Здравствуйте, rg45, Вы писали:

BFE>>5) Лямбда.


R>Даже не знаю, уместно ли вспоминать о лябмдах в данном контесте. Все-таки, лябмда — это элемент парадигмы, позволяющей оперирровать функциями как данными. Но никак не средство оформления. И по отношению к лямбдам возникают почти все те же вопросы, что и по отношению к функциям: какая область видимости, какое связывание, член или не член...


Все приведённые примеры темы имеют вызов из одного метода. В этом случае функцию можно оформить прямо внутри метода в виде лямбды (не захватывая никаких переменных): никаких проблем с видимостью, связыванием и членством.
И каждый день — без права на ошибку...
Re[4]: Внутренние функции класса
От: rg45 СССР  
Дата: 18.01.19 11:16
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Все приведённые примеры темы имеют вызов из одного метода. В этом случае функцию можно оформить прямо внутри метода в виде лямбды (не захватывая никаких переменных): никаких проблем с видимостью, связыванием и членством.


Можно-то можно, вот только нужно ли. Я понимаю, если лямбда передается параметром в какую-то другую функцию, тогда, конечно, ее использование обправдано, ибо ради этого лямбды и придуманы, собственно. А опредлить лямбду только для того, чтобы тут же ее и вызвать — польза от такого использования лично для меня сомнительна. Обычная свободная функция в анонимном пространстве имен — вот что самое оно для таких случаев — код получается и проще, и компактнее, и читабельнее. Хотя, все субъективно, конечно.
--
Re[5]: Внутренние функции класса
От: B0FEE664  
Дата: 18.01.19 13:41
Оценка:
Здравствуйте, rg45, Вы писали:

R>Можно-то можно, вот только нужно ли. Я понимаю, если лямбда передается параметром в какую-то другую функцию, тогда, конечно, ее использование обправдано, ибо ради этого лямбды и придуманы, собственно. А опредлить лямбду только для того, чтобы тут же ее и вызвать — польза от такого использования лично для меня сомнительна. Обычная свободная функция в анонимном пространстве имен — вот что самое оно для таких случаев — код получается и проще, и компактнее, и читабельнее. Хотя, все субъективно, конечно.


Чем плохо:
void Foo::Prn()
{
    auto fnEnabled = [](bool b) -> const char* { return b ? "enabled" : "disabled"; };

    std::cout << "filter   : " << fnEnabled(m_bFilter  ) << std::endl
              << "leds     : " << fnEnabled(m_bLeds    ) << std::endl
              << "detector : " << fnEnabled(m_bDetector) << std::endl;
}

?
И каждый день — без права на ошибку...
Re[6]: Внутренние функции класса
От: rg45 СССР  
Дата: 18.01.19 13:59
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Чем плохо:

BFE>
BFE>void Foo::Prn()
BFE>{
BFE>    auto fnEnabled = [](bool b) -> const char* { return b ? "enabled" : "disabled"; };

BFE>    std::cout << "filter   : " << fnEnabled(m_bFilter  ) << std::endl
BFE>              << "leds     : " << fnEnabled(m_bLeds    ) << std::endl
BFE>              << "detector : " << fnEnabled(m_bDetector) << std::endl;
BFE>}
BFE>

BFE>?

Ну вот не нравится, и все тут. Не для того аккумулятор покупали, чтоб капусту солить А потом приходится выслушивать, мол "переусложнизмом" С++ страдает. Так понятно, если применять возможности языка не по назначению, то так оно и будет восприниматься.

Нецелевое использование

--
Отредактировано 18.01.2019 14:07 rg45 . Предыдущая версия .
Re[6]: Внутренние функции класса
От: rg45 СССР  
Дата: 19.01.19 06:52
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Чем плохо:

BFE>
BFE>void Foo::Prn()
BFE>{
BFE>    auto fnEnabled = [](bool b) -> const char* { return b ? "enabled" : "disabled"; };

BFE>    std::cout << "filter   : " << fnEnabled(m_bFilter  ) << std::endl
BFE>              << "leds     : " << fnEnabled(m_bLeds    ) << std::endl
BFE>              << "detector : " << fnEnabled(m_bDetector) << std::endl;
BFE>}
BFE>

BFE>?

Ну, хорошо, согласен, полльза от такого применения лямбд есть: это позволяет давать функциям (лямбдам) максимально простые имена, не засоряя пространство имен, пусть даже анонимное. Но, что мне все же не нравится, это то, что мы исплоьзуем лямду не потому, что нам нужна лябмда, как таковая, а просто потому, что правила языка не позволяют нам определять функции внутри других функций. Нам же никогда не прийдет в голову написать лямбду в пространстве имен там, где достаточно обычной функции. Вот поэтому такое использование лямбд кажется мне нецелевым. Хотя и оправданным в некоторых случаях, таких как этот.
--
Отредактировано 19.01.2019 6:52 rg45 . Предыдущая версия .
Re[3]: Внутренние функции класса
От: Erop Россия  
Дата: 19.01.19 07:04
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

R>>Если это просто чистая функция, которой не нужен доступ к членам класса, то прямо в сипипи-шнике, в анонимном пространстве имен. При этом "static" и "inline" являются избыточными.


BFE>Почему inline является избыточным?


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


А слова вроде static и inline влияют на связывание, видимость, ODR и тому подобные вещи, а не на то, будет подставляться то, что видно в точке вызова или нет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Внутренние функции класса
От: Erop Россия  
Дата: 19.01.19 07:09
Оценка:
Здравствуйте, SaZ, Вы писали:

SaZ>Так я же и написал, что не спасают. В нашем случае поступали очень просто, в большинстве случаев делали static private функции внутри класса. Да, интерфейс загаживался, но это было не критично.



можно было просто для класса MyClass такие функции называть MyClass_f или положить их в namespace MyClass_private и не загаживать интерфейс.
Да, сделать это можно полуавтоматически, например скриптом.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Внутренние функции класса
От: IID Россия  
Дата: 19.01.19 21:27
Оценка: +1
Здравствуйте, landerhigh, Вы писали:

L>А можно наконец переехать в 21 век и начать писать тесты. Юнит-тесты, функциональные тесты и прочие...


Уже который год подряд ты всё подгораешь от отладчиков.
kalsarikännit
Re[4]: Внутренние функции класса
От: rg45 СССР  
Дата: 20.01.19 09:57
Оценка:
Здравствуйте, Erop, Вы писали:

E>Потому, что при типичных настройках оптимизации компилятор с линкером сами решают что подставлять, а что нет, главное что бы определение подставляемого (или иначе используемого, кстати) было в точке вызова доступно.


А последние несколько лет компиляторы умеют даже инлайнить функции, определение которых располагается после точки использования, в той же единице трансляции. Наиболее распространенный сценарий — это обращение к приватным функциям-членам изнутри класса — инлайнинг пройдет *как надо*, независимо от последовательности определений всех функций членов в рамках единицы трансляции. Блягодаря этому, можно не заморачиваться и выбирать последовательность определений, исходя из соображений лучшего восприятия кода.
--
Отредактировано 20.01.2019 10:07 rg45 . Предыдущая версия . Еще …
Отредактировано 20.01.2019 10:04 rg45 . Предыдущая версия .
Re[7]: Внутренние функции класса
От: Kswapd Россия  
Дата: 20.01.19 10:36
Оценка: +1
Здравствуйте, rg45, Вы писали:

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


Да вроде нормальное решение — обычная вложенная функция. Они бывают очень кстати как раз для уменьшения ненужной сложности: не засоряется scope файла. До лямбд было совсем не то: или изгаляться с макросами, или с локальными классами-функторами, которые громоздки и не имеют автоматического доступа к локальным переменным объемлющей функции; поэтому обычно и не заморачивались.

В Go, конечно, это делается элегантнее:
func something() {
    inc := func(x int) int { return x + 1 }
    i := 1
    fmt.Println(inc(i)) // prints 2
}
Re: Внутренние функции класса
От: prog123 Европа  
Дата: 20.01.19 20:11
Оценка:
Здравствуйте, Igore, Вы писали:

I>Добрый вечер, возник тут у меня вопрос, а как вы оформляете внутренние(служебные, чистые) функции класса.

...
I>Хочется совета, кто что использует или что было бы хорошо использовать.

pimpl еще посмотри.
Re[7]: Внутренние функции класса
От: SaZ  
Дата: 21.01.19 16:39
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну, хорошо, согласен, полльза от такого применения лямбд есть: это позволяет давать функциям (лямбдам) максимально простые имена, не засоряя пространство имен, пусть даже анонимное. Но, что мне все же не нравится, это то, что мы исплоьзуем лямду не потому, что нам нужна лябмда, как таковая, а просто потому, что правила языка не позволяют нам определять функции внутри других функций. Нам же никогда не прийдет в голову написать лямбду в пространстве имен там, где достаточно обычной функции. Вот поэтому такое использование лямбд кажется мне нецелевым. Хотя и оправданным в некоторых случаях, таких как этот.


Есть ещё такой вариант использования лямбд для инициализации констант:

const std::vector<int> v = [&]()
{
  std::vector<int> a;
  // Runtime initialization code
  while(...)
    if(...)
      a.push_back(...);
  return a;
}();

v - тут будет константой.
Re[8]: Внутренние функции класса
От: rg45 СССР  
Дата: 21.01.19 16:42
Оценка:
Здравствуйте, SaZ, Вы писали:

aZ>Есть ещё такой вариант использования лямбд для инициализации констант:


SaZ>
SaZ>const std::vector<int> v = [&]()
SaZ>{
SaZ>  std::vector<int> a;
SaZ>  // Runtime initialization code
SaZ>  while(...)
SaZ>    if(...)
SaZ>      a.push_back(...);
SaZ>  return a;
SaZ>}();

SaZ>v - тут будет константой.
SaZ>


Так ровно того же эффекта можно добиться при помощи обычныой функции
--
Re[8]: Внутренние функции класса
От: B0FEE664  
Дата: 21.01.19 16:53
Оценка: +2
Здравствуйте, SaZ, Вы писали:

SaZ>Есть ещё такой вариант использования лямбд для инициализации констант:

SaZ>
SaZ>const std::vector<int> v = [&]()
SaZ>{
SaZ>  std::vector<int> a;
SaZ>  // Runtime initialization code
SaZ>  while(...)
SaZ>    if(...)
SaZ>      a.push_back(...);
SaZ>  return a;
SaZ>}();

SaZ>v - тут будет константой.
SaZ>


В этом примере мне не нравится захват локальных переменных. Очень легко получить UB и не заметить:
const std::vector<int> v = [&]()
{
  std::vector<int> a;
  // Runtime initialization code
  while(v.size() != 0)
    if(true)
      a.push_back(1);
  a.push_back(2);
  return a;
}();
И каждый день — без права на ошибку...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.