6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
1

Как избежать дублирования кода при написании константного и не константного метода?

28.02.2022, 15:43. Показов 2403. Ответов 36
Метки c++ (Все метки)

Author24 — интернет-сервис помощи студентам
Есть класс Buffer, который содержит метод forBounds, который в качестве параметров принимает вспомогательный класс Bounds и шаблонный элемент _Func, который должен быть лямбда-функцией, в которой описывается логика. В общем, сам метод чем-то напоминаем функцию std::for_each. Проблема заключается в том, что мне нужно как-то сделать два метода const и не const версию. Дублировать код и писать макросы как-то не хочется.

Есть ли способ, как можно сделать два одинаковых по коду метода, но так, чтобы один был const, а другой нет, но при этом избежать дублирования?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
template<typename _T>
template<typename _Func>
inline bool Buffer<_T>::forBounds(const Bounds& bounds, const _Func& func) const
{
    if (!getBounds().isContain(bounds))
    {
        CERR_ERROR(STR_ERROR_VIOLATION_OF_BUFFER_BOUNDS)
        return false;
    }
    const size_t start_index = bounds.getStartCell().toIndex(m_dimension);
    const size_t finish_index = bounds.getFinishCell().toIndex(m_dimension);
    const size_t width = bounds.getWidth();
    const size_t step = getNumberOfColumns() - width;
    size_t current_col = 1;
    for (size_t i = start_index; i <= finish_index; i++)
    {
        func(at(i));
        if (current_col % width == 0)
        {
            current_col = 1;
            i += step;
        }
        else
            current_col++;
    }
    return true;
}
 
template<typename _T>
template<typename _Func>
inline bool Buffer<_T>::forBounds(const Bounds& bounds, const _Func& func)
{
    //Надо что-то сделать, чтобы не дублировать код.
}
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
28.02.2022, 15:43
Ответы с готовыми решениями:

Возврат константного значение из функции/метода
Здравствуйте подскажите пожалуйста как из функции или метода вернуть константное значение? что бы...

Выполнение метода в зависимости от константного свойства
class Z { private: const int num; public: Z(int val): num(val){} template&lt;int...

Как избежать дублирования кода при использовании virtual функций?
Здравствуйте. Есть Класс Студент_А и Студент_Б. В каждом есть поле private содержащее string name....

Избежать дублирования проверок при выполнении метода
Предположим, у банка есть метод который осуществляет перевод денег со счета на счет. В нем есть...

36
2327 / 1816 / 751
Регистрация: 27.07.2012
Сообщений: 5,357
28.02.2022, 16:03 2
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
 
class C
{
public:
    void func() { std::cout << "non-const func"; }
    void const_func() const { const_cast<C*>(this)->func(); }
};
 
void f(const C & c) {
    c.const_func();
}
 
int main()
{
    C c;
    f(c);
}
Добавлено через 1 минуту
А в вашем случае можно просто из неконстантного метода вызвать константный как обычно.
0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 16:20  [ТС] 3
John Prick, если я из неконстантного метода вызываю константный, то происходит stack overflow, а если я сделаю std::as_const(*this).forBounds, то смысла от неконстантного метода нет, т.к. параметр типа _Func, который является лямбда функцией не сможет изменять состояния буффера.
0
2327 / 1816 / 751
Регистрация: 27.07.2012
Сообщений: 5,357
28.02.2022, 16:31 4
Цитата Сообщение от Аким2020 Посмотреть сообщение
если я из неконстантного метода вызываю константный, то происходит stack overflow
Это не из-за константности/неконстантности. Ищите ошибку в коде.

Добавлено через 6 минут
Другое дело, что если у вас константный метод должен что-то изменять в объекте, то почему он тогда константный?
0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 16:33  [ТС] 5
Есть неконстантный объект класса Buffer. Я для него вызываю метод forBounds, который имеет в себе след. строчку->
C++
1
return forBounds
то есть я из не константного метода вызываю опять не константный и оттуда stack overflow. Дабы это избежать можно явно указать, что я буду вызвать этот метод для const this. Для этого я делаю std::as_const, но тогда смысла от неконстантного метода нет, потому что я все равно вызову константный метод и не смогу изменить состояния объекта.

Добавлено через 1 минуту
John Prick, константный метод не должен ничего изменять, он принимает лямбду. Если метод константный, то лямбда не сможет ничего изменить и будет ошибка.
0
2327 / 1816 / 751
Регистрация: 27.07.2012
Сообщений: 5,357
28.02.2022, 16:36 6
Цитата Сообщение от Аким2020 Посмотреть сообщение
константный метод не должен ничего изменять, он принимает лямбду. Если метод константный, то лямбда не сможет ничего изменить и будет ошибка.
Ничего не понятно. Приведите пример кода, где в метод передаётся лямбда.
0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 16:39  [ТС] 7
John Prick,
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Buffer<int> buffer =
    {
        {1,2,3,4,5},
        {6,7,8,9,10},
        {11,12,13,14,15},
        {16,17,18,19,20},
        {21,22,23,24,25}
    };
    auto bounds = Bounds({ 0,0 }, { 2,2 });
 
    buffer.forBounds(bounds, [&](int& el) 
        {
            static size_t column = 1;
            el = 2;
            std::cout << el << " ";
            if (column % bounds.getWidth() == 0)
            {
                column = 1;
                std::cout << std::endl;
            }
            else
                column++;
        }
    );
0
2327 / 1816 / 751
Регистрация: 27.07.2012
Сообщений: 5,357
28.02.2022, 16:51 8
Ну тут проблема в том, что в константном методе вызывается at(i), которая возвращает const int &, тогда как лямбда принимает int& для того, чтобы изменить его. Собственно, тут и возникает вопрос, зачем в константном методе изменять объект. Если это нужно, сделайте метод неконстантным, и всё.
0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 17:01  [ТС] 9
John Prick, метод at имеет конст и неконст перегрузки. Мне нужно, чтобы методом forBounds можно было пользоваться, как с целью изменения состояния буффера, так и способом частичного обхода коллекции без изменения состояния самого буффера. Я бы мог воспользоваться паттерном итератор, но, как по мне, это уже будет оверинженеринг. Неужели остается писать макрос под целый метод?
0
2327 / 1816 / 751
Регистрация: 27.07.2012
Сообщений: 5,357
28.02.2022, 17:16 10
Цитата Сообщение от Аким2020 Посмотреть сообщение
метод at имеет конст и неконст перегрузки
Да, имеет. Но в const методе вызывается const версия at, так как тип this в таком методе - const Bounds * const.

Добавлено через 3 минуты
А зачем вам в лямбде всё окружение передавать [&], когда используется только bounds.getWidth()? Передайте это число либо параметром, либо [w = bounds.getWidth()]. Сложно же разбираться в таких связях будет.
0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 17:23  [ТС] 11
John Prick, я на это и рассчитывал. Например, я захочу обойти буффер в заданных границах, но не хочу изменять его состояние. Для этого бы, желательно, вызвать const версию forBounds. При вызове этого метода я не смогу изменить лямбдой состояния буффера - будет ошибка компиляции. Для случая, когда объект не const, мне нужен метод forBounds, который смог бы принять лямбу, которая сможет изменить состояние буффера. При дублировании кода все работает замечательно, но мне таааак не хочется этого делать... А макрос на целый метод уродство ужасное. Может, есть способ сбросить const спецификатор у метода? В гугле все предлагают кастовать this, но это не подходит.

Добавлено через 1 минуту
Цитата Сообщение от John Prick Посмотреть сообщение
А зачем вам в лямбде всё окружение передавать [&], когда используется только bounds.getWidth()? Передайте это число либо параметром, либо [w = bounds.getWidth()]. Сложно же разбираться в таких связях будет.
для примера быстро накидал, даже не задумывался.
0
фрилансер
5826 / 5346 / 1097
Регистрация: 11.10.2019
Сообщений: 14,284
28.02.2022, 18:19 12
Цитата Сообщение от John Prick Посмотреть сообщение
const_cast<C*>
не советую такое использовать, можно такущие грабли отхватить То же самое касается mutable полей класса
Единственный случай, когда мне пришлось применить mutable - при наследовании виртуального метода в Qt. Метод был константный, и это поменять невозможно.

Аким2020,
а как метод может быть одинаковым и для константного объекта, и для не константного?
Тут что-то одно может быть. Либо тело функции всё же должно быть разное

в лямбду можно захватить неконстантный указатель на буфер - это будет решением.
0
2327 / 1816 / 751
Регистрация: 27.07.2012
Сообщений: 5,357
28.02.2022, 18:30 13
Цитата Сообщение от Алексей1153 Посмотреть сообщение
не советую такое использовать, можно такущие грабли отхватить То же самое касается mutable полей класса.
Да я не говорю, что так надо делать, просто так технически возможно. Но ТС говорит, что у него так тоже не работает.

А mutable вполне можно использовать для тех частей класса, которые при вызове константных методов должны изменяться, но при этом логическое состояние самого объекта не меняется. Например, блокировки при многопоточном доступе.
0
фрилансер
5826 / 5346 / 1097
Регистрация: 11.10.2019
Сообщений: 14,284
28.02.2022, 18:38 14
John Prick, вот именно из-за многопоточности и опасно mutable использовать. Если мутекс имеет два режима блокировки - только чтение или только запись, то во время readonly можно по невнимательности поменять значение mutable-поля. И привет
0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 20:11  [ТС] 15
Алексей1153,
Цитата Сообщение от Алексей1153 Посмотреть сообщение
а как метод может быть одинаковым и для константного объекта, и для не константного?
Тут что-то одно может быть. Либо тело функции всё же должно быть разное
тело самого метода одинаковое, как для конст, так и не конст. Тут проблема заключается в лямбде, которая для константного буффера не должна иметь возможность менять состояние объекта, а для не константного - нет. Вот я и не знаю, что делать... Можно, конечно, отдельной функцией реализовать и передавать буффер в качестве параметра, но мне хочется, чтоб это был метод класса.
0
фрилансер
5826 / 5346 / 1097
Регистрация: 11.10.2019
Сообщений: 14,284
28.02.2022, 21:04 16
Цитата Сообщение от Аким2020 Посмотреть сообщение
Тут проблема заключается в лямбде, которая для константного буффера не должна иметь возможность менять состояние объекта, а для не константного - нет.
так в чём проблема то ? Захват будет с таким же квалификатором, как и захваченный объект

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Buffer
{
    size_t N{};
};
 
int main()
{
    Buffer b1;
    
    const Buffer b2;
    
    auto l1=[&b1]
    {
        b1.N=10;//ок
    };
 
    auto l2=[&b2]
    {
        b2.N=10;//ошибка
    };
 
    return 0;
}
0
SmallEvil
28.02.2022, 21:10
  #17

Не по теме:

Алексей1153, список аргументов лямбды можно опускать ?

0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 21:21  [ТС] 18
Алексей1153, проблема в том, что я не хочу захватывать буффер лямбдой. Лямбда - это та функция, которая будет применена для каждого элемента коллекции текущего объекта. Проще понять на методе, который бы назывался forEachElement(const _Func& func). В таком виде(без спецификации const) лямбда сможет изменить каждый элемент. Теперь я хочу сделать такой же метод, но чтоб можно было обходить все элементы коллекции, но без возможности изменять элементы, поэтому добавляю к методу спецификацию const. Теперь у меня есть проблема дублирования кода. Внутренности двух методов абсолютно одинаковые, но вот лямбды должны быть разные. В случае const метода, принимаемая лямбда не должна иметь возможность менять элементы коллекции, а в не const - может менять. Вопрос, как мне избежать дублирования кода?
0
фрилансер
5826 / 5346 / 1097
Регистрация: 11.10.2019
Сообщений: 14,284
28.02.2022, 21:31 19
SmallEvil, можно, но в некоторых случаях всё равно необходимо оставлять скобки (например, при подсказке возвращаемого типа, при указании mutable или noexcept)

Аким2020, если речь о производительности, то захват у меня показан по ссылке, а не по значению.

насчёт константности - можно принудительно передать константную ссылку или указатель, если это нужно
0
6 / 5 / 1
Регистрация: 05.10.2020
Сообщений: 122
28.02.2022, 23:29  [ТС] 20
В общем, я ничего лучше не придумал, чем сделать макрос, который выглядит так:
C++
1
2
3
4
5
#define CONST_SPECIFIC_DUPLICATE(_template_params_, _declaration_, _body_) \
_template_params_ \
_declaration_ _body_ \
_template_params_\
_declaration_ const _body_
Объявляю метод как обычно:
C++
1
2
3
4
template<typename _Func>
bool forBounds(const Bounds& bounds, const _Func& func) const;
template<typename _Func>
bool forBounds(const Bounds& bounds, const _Func& func);
А определяю так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
CONST_SPECIFIC_DUPLICATE
(
    template<typename _T>
    template<typename _Func>,
    inline bool Buffer<_T>::forBounds(const Bounds& bounds, const _Func& func),
    {
        if (!getBounds().isContain(bounds))
        {
            CERR_ERROR(STR_ERROR_VIOLATION_OF_BUFFER_BOUNDS)
            return false;
        }
        const Dimension dimension = getDimension();
        const size_t start_index = bounds.getStartCell().toIndex(dimension);
        const size_t finish_index = bounds.getFinishCell().toIndex(dimension);
        const size_t width = bounds.getWidth();
        const size_t step = getNumberOfColumns() - width;
        size_t current_col = 1;
        for (size_t i = start_index; i <= finish_index; i++)
        {
            func(at(i));
            if (current_col % width == 0)
            {
                current_col = 1;
                i += step;
            }
            else
                current_col++;
        }
        return true;
    }
)
Если есть идеи лучше, пожалуйста, скажите мне. Просто, я нахожу свое решение немного уродским.
0
28.02.2022, 23:29
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
28.02.2022, 23:29
Помогаю со студенческими работами здесь

Как избежать дублирования кода?
Приветствую Вас уважаемые форумчане! Хочу обратиться к Вам за помощью. Возникли огромные пробелы...

Как избежать дублирования кода в Си
Допустим, есть такие структуры данных: enum Color{ Red, Blue, Black, White };...

Как избежать дублирования кода? Наследование
Здравствуйте. У меня следующая проблема: Есть несколько классов: A, B, C. Они наследуются от...

Ошибка при выводе константного значения С++
Всем привет! Столкнулся с проблемкой, выводит вот такую ошибку. Ошибка C2679 бинарный &quot;&lt;&lt;&quot;: не...

Ошибка при обращении к методу константного объекта
Есть некий класс, в нем есть метод const size_t Length(); пишу оператор сложения Class&amp; operator...

Усечение константного значения при присвоении значений объектам структуры
Здравствуйте! Есть вот такой код: #include &lt;iostream&gt; using namespace std; struct date {...

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru