2015-08-05 1 views
1

Это конкретный случай this question, где that answer не работает напрямую.Как избежать дублирования кода между аналогичными функциями const и non-const, которые передают членам класса обратные вызовы?

Consider:

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& во втором дело.

В духе ранее связанный к Scott Meyers' solution, я придумал следующее:

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

Вы можете использовать шаблон функции друга с идеальной пересылкой. – dyp

+0

Не могли бы вы прояснить разницу между вашим вопросом и тем, который вы указали? Я вижу, что вы используете шаблон функции (участника), тогда как в другом вопросе используется «обычная» (членная) функция; но есть подходы, которые работают для обоих. – dyp

+0

@ dyp: Ahh [нравится так] (https://ideone.com/i4AVT4)? В основном, два лучших ответа там не будут работать в этом случае (потому что обратный вызов в неконстантной версии все равно будет передаваться константными переменными-членами), поэтому я задавался вопросом, был ли более хороший способ сделать это, чем мое решение. Мне нравится подход к шаблону функции друзей - я не думаю, что там есть функции ответа. (На самом деле кажется приятнее, если я делаю это статической частной функцией.) – Claudiu

ответ

3

Вы можете использовать статический шаблон функции члена направить *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. Это не всегда важно, поскольку вы часто не хотите, чтобы функции-члены вызывались на rvalues. Я добавлю это здесь, так как считаю, что это часть более общего решения.

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

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. Вероятно, это не стоит усилий.

Смежные вопросы