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

Как сравнить несколько аргументов в одной строке без лишнего копирования, я буду сравнивать больше 2 аргументов, возможно их будет 5.

У меня есть пару аргументов например.

char A1 = 1, B2 = 2, C3 = 3, D4 = 4;

Я бы хотел это делать так

if (function == A1 || B2) {

 std::cout << A1 << " " << B2 << std::endl;

}

Но приходиться делать это так

if (function == A1 || function ==  B2) {

 std::cout << A1 << " " << B2 << std::endl;

}

Я бы не хотел каждый раз повторять function == Можно ли как то это упростить?

Или как вариант покажите как вы делаете это с enum, главное что бы код был минималистичный


Ответы (6 шт):

Автор решения: user7860670
template<typename x_Value, typename... x_Args>
auto cmp(x_Value const & value, x_Args const &... args)
{
    return ((value == args) or ...);
}

#include <cassert>

int main()
{
    auto a{3};
    auto b{5};
    auto c{3};
    auto d{6};
    assert(cmp(a, b, c, d));
    return 0;
}

online compiler

Стоит сказать, что в таком решении вычисление всех операндов будет происходить всегда, тогда как в случае простого (a == b) or (a == c) || (a == d) они будут вычисляться только при необходимости.

→ Ссылка
Автор решения: user7860670

Вариант с перегрузкой оператора.

#include <utility>

template<typename x_LeftValue>
struct
t_Eq
{
    x_LeftValue const & value;
    bool result;

    template<typename x_RightValue>
    explicit t_Eq(x_LeftValue const & left, x_RightValue const & right)
    : value{left}, result{value == right} {}

    explicit operator bool (void) const noexcept { return result; }

    template<typename x_RightValue>
    [[nodiscard]] auto && operator ,(x_RightValue const & right) &&
    {
        result = result or (value == right);
        return ::std::move(*this);
    }
};

template<typename x_LeftValue>
struct
t_With
{
    x_LeftValue const & value;

    explicit t_With(x_LeftValue const & left) noexcept : value{left} {}

    template<typename x_RightValue>
    [[nodiscard]] auto operator ==(x_RightValue const & right) &&
    {
        return t_Eq{value, right};
    }
};

template<typename x_Value>
[[nodiscard]] auto with(x_Value const & value) noexcept
{
    return t_With{value};
}

#include <iostream>
#include <cassert>

int main()
{
    auto a{3};
    auto b{5};
    auto c{9};
    auto d{3};
    if (with(a) == b, c, d)
    {
        ::std::cout << "true" << ::std::endl;
    }
    else
    {
        ::std::cout << "false" << ::std::endl;
    }
    return 0;
}

online compiler

→ Ссылка
Автор решения: Bearded Beaver

Предложу чуть более простой вариант:

char a = 1;
char b = 2;
char c = 3;
char d = 5;

auto values = {a, b, c, d};
bool result = std::any_of(values.begin(), values.end(), [&](auto value){return value == 2;});
std::cout << result;

Плюсы:

  • Каноничное использование STL
  • Нет необходимости тащить дополнительную зависимость и добавлять мутные перегрузки операторов
  • Можно не просто сравнивать на равенство, но и добавлять более сложные условия типа value % 2 == 1
  • Можно использовать std::all_of или std::none_of

Минусы:

  • Требует копирования сравниваемых переменных (не критично для примитивных типов, но если надо сравнить что-то дорого копируемое, будет печально)
  • Итоговый синтаксис совсем не такой, как спрашивал автор (хотя кмк более явно выражающий намерение программиста)
→ Ссылка
Автор решения: Stanislav Volodarskiy

Списки инициализации обеспечивают достаточно компактный синтаксис:

template<class T>
bool in(const T &x, std::initializer_list<T> c) {
    return std::find(c.begin(), c.end(), x) != c.end();
}

...
    char A1 = 1, B2 = 2, C3 = 3;

    char function = ...;
    if (in(function, {A1, B2, C3})) {
        ...
    }
...

У оптимизатора есть возможность развернуть код в последовательность:

#include <algorithm>
#include <initializer_list>

template<class T>
bool in(const T &x, std::initializer_list<T> c) {
    return std::find(c.begin(), c.end(), x) != c.end();
}

bool check(char function) {
    char A1 = 1, B2 = 2, C3 = 3;
    return in(function, {A1, B2, C3});
}

Цикла в ассемблере нет, а есть три сравнения с тремя константами:

$ g++ -std=c++17 -pedantic -Wall -Wextra -Werror -O3 -S temp.cpp
$ cat temp.s

  .file   "temp.cpp"
  .text
  .p2align 4
  .globl  _Z5checkc
  .type   _Z5checkc, @function
_Z5checkc:
.LFB3008:
  .cfi_startproc
  movl    $1, %eax
  cmpb    $1, %dil
  je  .L1
  cmpb    $2, %dil
  je  .L1
  cmpb    $3, %dil
  sete    %al
.L1:
  ret
  .cfi_endproc
.LFE3008:
  .size   _Z5checkc, .-_Z5checkc
  .ident  "GCC: (Ubuntu 9.4.0-1ubuntu1~16.04) 9.4.0"
  .section    .note.GNU-stack,"",@progbits
→ Ссылка
Автор решения: AlexGlebe

Если у вас точно сравнение идёт байтовое и без проверки на нуль, то самый простой способ это использовать функцию поиска буквы в строке.

Создаёте массив байт с завершающим нулевым

char arr [ ] = { A1 , B2 , C3 , D4 , E5 , 0 } ;

и вызываете функцию strchr , она возвращает указатель на найденные байт или nullptr.

# include <iostream>
# include <cstring>
int main(){
    char f = 2 ;
    char A1 = 1, B2 = 2, C3 = 3, D4 = 4, E5=5;
    char arr [ ] = { A1 , B2 , C3 , D4 , E5 , 0 } ;
    if ( strchr ( arr , f ) )
      std::cout<<(int)f<<" присутствует в массиве"<<std::endl;
    else
      std::cout<<(int)f<<" не найден в массиве"<<std::endl;
}
→ Ссылка
Автор решения: Stanislav Volodarskiy

В вопросе упоминался enum. Если значения не большие, можно перейти к позициям битов:

int A1 = 1 << 1, B2 = 1 << 2, C3 = 1 << 3;

...
char bit = function << 3;
if ((bit & (A1 | B2 | C3)) != 0) {
    ...
}

uint64_t позволит работать с диапазоном [0, 63]. Нотация достаточно компактная и читабельная (для любителей С). С точки зрения скорости исполнения - это вообще одна инструкция процессора (ещё одна для function << 3, но можно работать сразу с величинами вида 1 << n_bit без промежуточных преобразований).

→ Ссылка