Ваша проблема в том, что компилятору необходимо знать, какую ветвь условия использовать во время компиляции, потому что функции имеют разные сигнатуры. Таким образом, у вас есть либо решение, данное @TrebuchetMS, то есть принимать только функции с индексом. Или вы должны как-то выразить свои намерения в системе типов.
Я вижу три возможных решения, но, вероятно, есть и другие:
1) Перегрузка LoopRangeA
для обоих типов функций, таких как:
inline void LoopRangeA(void (*f)(MyStruct*)) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
inline void LoopRangeA(void (*f)(MyStruct*, size_t)) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
}
Это выберет тип цикла в зависимости от сигнатуры функции. Недостатком является то, что вам, вероятно, придется предоставлять перегрузки и для onst Mystruct*
.
2) Поскольку вы можете использовать C ++ 17, вы можете использовать if constexpr
, предоставив параметр шаблона bool
:
template<bool GetIndex, typename FUNCTION>
void LoopRangeA1(FUNCTION f) {
if constexpr(GetIndex) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
} else {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
}
Но, как @StoryTeller упоминает в своем комментарии, вы должны передавать избыточную информацию, так как в любом случае потребность в индексе закодирована в сигнатуре функции.
3) Поэтому я бы предпочел 3-е решение, которое использует лучшее из обоих:
Сначала вы предоставляете функцию, которая определяет возможность использования индекса во время компиляции. для этого нужно немного хитрости в constexpr:
constexpr std::false_type eats_index(...) { return {}; }
template<typename T>
constexpr auto eats_index(T) -> decltype(std::declval<T>()(std::declval<MyStruct*>(), 0), std::true_type{}) {
return {};
}
Затем вы реализуете свою функцию следующим образом:
template<typename FUNCTION>
void LoopRangeA2(FUNCTION f) {
if constexpr(eats_index(f)) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
} else {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
}
Наконец, это функция main
, показывающая, как использовать решения:
int main() {
MyClass Cls = MyClass();
auto print_no_idx = [](MyStruct* pStr) {pStr->PrintValue(); };
auto print_const_no_idx = [](const MyStruct* pStr) { };
auto print_with_idx = [](MyStruct* pStr, size_t idx) {
std::cout << "index: " << idx << " -> ";
pStr->PrintValue(); };
Cls.LoopRangeA(print_no_idx);
Cls.LoopRangeA(print_const_no_idx); // <- does not compile, you'd need another overload
Cls.LoopRangeA(print_with_idx);
Cls.LoopRangeA1<false>(print_no_idx);
Cls.LoopRangeA1<false>(print_const_no_idx); // <- works w/o additional overload
Cls.LoopRangeA1<true>(print_with_idx);
static_assert(!eats_index(print_no_idx));
static_assert(eats_index(print_with_idx));
Cls.LoopRangeA2(print_no_idx);
Cls.LoopRangeA2(print_const_no_idx); // <- works, w/o additional overload
Cls.LoopRangeA2(print_with_idx);
std::cout << "Application is finished. Press ENTER to exit..." << std::endl;
std::cin.get();
}
См. здесь для полного примера.