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

Это частный случай этот вопрос где этот ответ не работает напрямую

Рассматривать:

struct hurg {};

class furg {
public:
template <class F>
void for_each_hurg(F&& f) const {
for (auto& h : hurgs) {
f(h);
}
}

template <class F>
void for_each_hurg(F&& f) {
for (auto& h : hurgs) {
f(h);
}
}
private:
std::vector<hurg> hurgs;
};

Использование:

furg f;
const auto& cf = f;

f.for_each_hurg([](hurg& h) { });
cf.for_each_hurg([](const hurg& h) { });

Код для const и неconst версии идентичны, но только потому, что auto& h делает вывод, const hurg& в первом случае и hurg& во втором случае.

В духе ранее связанных с Решение Скотта Мейерса, Я придумал следующее:

template <class F>
void for_each_hurg(F&& f) {
const_cast<const furg&>(*this).for_each_hurg([&f](const hurg& h) {
f(const_cast<hurg&>(h));
});
}

Тем не менее, кажется, что это может быть больше проблем, чем стоит, особенно если типы длинные и если я не могу использовать общие лямбда-выражения C ++ 14.

1

Решение

Вы можете использовать статический шаблон функции-члена для пересылки *this к параметру универсальной функции:

template<typename Self, typename F>
static void for_each_hurg(Self& s, F&& f) {
for (auto& h : s.hurgs) {
f(h);
}
}

template<typename F>
void for_each_hurg(F&& f) { for_each_hurg(*this, forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) const { for_each_hurg(*this, forward<F>(f))); }

С появлением ссылочных квалификаторов для функций-членов общее решение заключается в в совершенстве вперед *this, Это не всегда важно, так как вы все равно не хотите, чтобы функции-члены вызывались для значений rvalue. Я добавлю это здесь, так как я думаю, что это является частью более общего решения.

К несчастью, *this всегда является lvalue, поэтому вам нужно выполнить дополнительный ручной уход в оболочках функций-членов:

template<typename Self, typename F>
static void for_each_hurg(Self&& s, F&& f) {
/* ... */
}

template<typename F>
void for_each_hurg(F&& f) && { for_each_hurg(move(*this), forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) & { for_each_hurg(*this, forward<F>(f))); }

Что, к сожалению, не симметрично 🙁


Также возможно реализовать вышеупомянутое через шаблон функции друга. Это может иметь два преимущества:

  • Вы можете переместить шаблон функции друга в область имен, что в случае furg Быть шаблоном класса уменьшает количество шаблонов функций, с которыми приходится иметь дело компилятору (по одному на шаблон класса, а не по одному на экземпляр). Однако обычно для этого требуется некоторый шаблонный код и предварительные декларации.
  • Вы можете вызывать функцию с тем же именем либо как свободную функцию, либо как функцию-член, например, furg f; for_each_hurg(furg, [](hurg){}); furg.for_each_hurg([](hurg){}); (Когда неквалифицированный поиск находит функцию-член, он не выполняет / игнорирует результаты ADL. Следовательно, вам нужно поместить функцию Friend в область пространства имен, чтобы иметь возможность обращаться к ней через квалифицированный идентификатор. из оберток нестатических функций-членов.)

Кроме того, вы должны защитить этот шаблон функции от жадности; либо положить его в какой-то namespace detail или добавление условия enable-if. Это, вероятно, не стоит усилий.

3

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector