Получить значение из «каскадного ifs» сложить выражение

Давайте предположим, что я хочу создать свой собственный лямбда-основанный коммутатор со следующим синтаксисом:

auto s = make_switch(std::pair{0, []{ return 0;   }},
std::pair{1, []{ return 50;  }},
std::pair{2, []{ return 100; }});

assert( s(0) == 0   );
assert( s(1) == 50  );
assert( s(2) == 100 );

Я хотел бы использовать сложить выражение чтобы иметь краткую реализацию, которая не требует рекурсии. Идея состоит в том, чтобы создать нечто похожее на кучу вложенных if заявления:

if(x == 0) return 0;
if(x == 1) return 50;
if(x == 2) return 100;

Я хотел бы написать это:

// pseudocode
template <typename... Pairs>
auto make_switch(Pairs... ps)
{
return [=](int x)
{
( if(ps.first == x) return ps.second(), ... );
};
}

Код выше не работает как if(...){...} это не выражение. Затем я попытался использовать && оператор:

template <typename... Pairs>
auto make_switch(Pairs... ps)
{
return [=](int x)
{
return ((ps.first == x && ps.second()), ...);
};
}

Это компилируется, но возвращает результат ps.first == x && ps.second(), который является bool а не int ценность, которую я хочу.

Я хотел бы какой-то оператор, который представляет собой комбинацию между оператор запятой а также &&: следует оценить и оценить до Правая сторона оператора, если левая сторона оценивает true,

Я не могу придумать какую-либо технику, которая позволила бы мне реализовать это таким образом, чтобы я мог получить ps.second()возвращаем значение и распространяем его на вызывающую лямбду, возвращаемую make_switch,

Возможно ли реализовать этот вид «каскадирования»? ifS «шаблон с сложить выражение? Я хотел бы оценить только столько выражений, сколько требуется, пока не будет найдена соответствующая ветвь.

7

Решение

Я удивлен, что это не было предложено еще:

template <typename ...Pairs> auto make_switch(Pairs ...ps)
{
return [=](int x)
{
int ret;
((x == ps.first && (void(ret = ps.second()), 1)) || ...)
/* || (throw whatever, 1) */ ;
return ret;
};
}

(попробуйте онлайн)

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

Короткое замыкание || используется для остановки функции при обнаружении совпадения.

(Для приведенного выше кода GCC 7.2 дает мне warning: suggest parentheses around '&&' within '||', Вероятно, ошибка?)

Редактировать:

Вот версия, обобщенная для любых типов: (кредиты @Barry за предложение std::optional)

template <typename InputType, typename ReturnType, typename ...Pairs> auto make_switch(Pairs ...ps)
{
/* You could do
*   using InputType  = std::common_type_t<typename Pairs::first_type...>;
*   using ReturnType = std::common_type_t<decltype(ps.second())...>;
* instead of using template parameters.
*/

return [=](InputType x)
{
std::optional<ReturnType> ret /* (default_value) */;
( ( x == ps.first && (void(ret.emplace(std::move(ps.second()))), 1) ) || ...)
/* || (throw whatever, 1) */;
return *ret;
};
}

(попробуйте онлайн)

Я решил использовать параметры шаблона для параметров и возвращаемых типов, но вы можете вывести их, если хотите.

Обратите внимание, что если вы решили не иметь значение по умолчанию, ни throw, затем передача неверного значения на коммутатор даст вам UB.

14

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

Это невозможно. Чтобы использовать выражение сгиба, вам нужно определить бинарный оператор на вашем Pairs,

В вашем случае такой бинарный оператор не может существовать, так как:

  • это должно быть statefull (то есть захват x, так как он сравнивает Pairs::first с x)
  • а также оператор должен быть (i) нестатической функцией-членом или (ii) не-функцией-членом.

Более того:

  • (i) оператор нестатического члена неявно принимает this в качестве первого аргумента, и вы не могли бы сделать this указатель на Pairs или производная от Pairs;
  • (ii) функция, не являющаяся членом, не смогла получить значение x,
0

Я думаю, что решение от HolyBlackCat лучше, но … как насчет использования суммы?

template <typename ... Pairs>
auto make_switch (Pairs ... ps)
{
return [=](int x)
{ return ( (ps.first == x ? ps.second() : 0) + ... ); };
}

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

0
По вопросам рекламы [email protected]