Как сравнить несколько аргументов в одной строке без лишних повторений?
Как сравнить несколько аргументов в одной строке без лишнего копирования, я буду сравнивать больше 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 шт):
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;
}
Стоит сказать, что в таком решении вычисление всех операндов будет происходить всегда, тогда как в случае простого (a == b) or (a == c) || (a == d) они будут вычисляться только при необходимости.
Вариант с перегрузкой оператора.
#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;
}
Предложу чуть более простой вариант:
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
Минусы:
- Требует копирования сравниваемых переменных (не критично для примитивных типов, но если надо сравнить что-то дорого копируемое, будет печально)
- Итоговый синтаксис совсем не такой, как спрашивал автор (хотя кмк более явно выражающий намерение программиста)
Списки инициализации обеспечивают достаточно компактный синтаксис:
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
Если у вас точно сравнение идёт байтовое и без проверки на нуль, то самый простой способ это использовать функцию поиска буквы в строке.
Создаёте массив байт с завершающим нулевым
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;
}
В вопросе упоминался 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 без промежуточных преобразований).