Ответ 1
Если вы хотите узнать, является ли целое число в заданном наборе целых чисел, используйте std::set
:
std::set<int> accept { 1, 4, 6, 8, 255, 42 };
int x = 1;
if (!accept.count(x))
{
// ...
}
Есть ли способ сократить условие для этого оператора if
?
int x;
if (x != 3 && x != 8 && x != 87 && x != 9){
SomeStuff();
}
Я думаю о чем-то вроде этого:
if (x != 3, 8, 87, 9) {}
Но я пробовал это, и он не работает. Должен ли я просто написать все это долгим путем?
Если вы хотите узнать, является ли целое число в заданном наборе целых чисел, используйте std::set
:
std::set<int> accept { 1, 4, 6, 8, 255, 42 };
int x = 1;
if (!accept.count(x))
{
// ...
}
Просто для полноты, я предлагаю использовать переключатель:
switch (x) {
case 1:
case 2:
case 37:
case 42:
break;
default:
SomeStuff();
break;
}
Пока это довольно многословно, он оценивает только x
один раз (если это выражение) и, вероятно, генерирует наиболее эффективный код любого решения.
Вот мое решение с использованием шаблона variadic. Производительность во время выполнения так же эффективна, как запись вручную x != 3 && x != 8 && x != 87 && x != 9
.
template <class T, class U>
bool not_equal(const T& t, const U& u) {
return t != u;
}
template <class T, class U, class... Vs>
bool not_equal(const T& t, const U& u, const Vs&... vs) {
return t != u && not_equal(t, vs...);
}
int main() {
std::cout << not_equal( 3, 3, 8, 87, 9) << std::endl;
std::cout << not_equal( 8, 3, 8, 87, 9) << std::endl;
std::cout << not_equal(87, 3, 8, 87, 9) << std::endl;
std::cout << not_equal( 9, 3, 8, 87, 9) << std::endl;
std::cout << not_equal(10, 3, 8, 87, 9) << std::endl;
}
Начиная с С++ 17, реализация может быть упрощена с помощью выражений складывания:
template <class T, class... Vs>
bool not_equal(const T& t, const Vs&... vs) {
return ((t != vs) && ...);
}
Как насчет этого:
#include <iostream>
#include <initializer_list>
#include <algorithm>
template <typename T>
bool in(const T t, const std::initializer_list<T> & l) {
return std::find(l.begin(), l.end(), t) != l.end();
}
int main() {
std::cout << !in(3, {3, 8, 87, 9}) << std::endl;
std::cout << !in(87, {3, 8, 87, 9}) << std::endl;
std::cout << !in(10, {3, 8, 87, 9}) << std::endl;
}
или перегрузка operator!=
:
template<typename T>
bool operator!=(const T t, const std::vector<T> & l) {
return std::find(l.begin(), l.end(), t) == l.end();
}
int main() {
std::cout << ( 3!=std::vector<int>{ 3, 8, 87, 9}) << std::endl;
std::cout << ( 8!=std::vector<int>{ 3, 8, 87, 9}) << std::endl;
std::cout << (10!=std::vector<int>{ 3, 8, 87, 9}) << std::endl;
}
К сожалению, в данный момент парсеры не любят иметь initializer_list
в качестве аргумента операторов, поэтому невозможно избавиться от std::vector<int>
во втором решение.
Если вы не хотите повторять это условие снова и снова, используйте макрос.
#include <iostream>
#define isTRUE(x, a, b, c, d) ( x != a && x != b && x != c && x != d )
int main()
{
int x(2);
std::cout << isTRUE(x,3,8,87,9) << std::endl;
if ( isTRUE(x,3,8,87,9) ){
// SomeStuff();
}
return 0;
}
Если числа не являются "1,2,3,4" и вместо этого представляют собой случайное число случайных чисел, то вы можете поместить эти числа в структуру данных (например, std::vector
), а затем итерации по этому массиву с использованием цикла (как предложено ниже, std:: find - готовый вариант).
Например:
#include <algorithm>
int x;
std::vector<int> checknums;
// fill the vector with your numbers to check x against
if (std::find(checknums.begin(), checknums.end(), x) != checknums.end()){
DoStuff();
}
int A[]={3, 8, 87, 9};
int x;
if (std::find(A, A+4, x)==A+4) SomeStuff();
Обратите внимание, что использование макросов сильно осуждается многими программистами С++, так как макросы очень мощные и могут быть расстроены, если они созданы ненадлежащим образом. Однако, если они созданы и используются должным образом и разумно, они могут стать важной экономией времени. Я не вижу другого способа получить запрошенный синтаксис и кодовое пространство на сравнение, аналогичное тому, что вы запрашиваете, без ущерба для эффективности, пространства кода или памяти, используемой вашей программой. Ваша цель состояла в том, чтобы иметь ярлык, и большинство других решений, представленных здесь, длиннее того, что вы изначально хотели сократить. Вот макросы, которые сделают это безопасно, предполагая, что вы сравниваете целое число:
#pragma once
int unused;
#define IFNOTIN2(x, a, b) \
if (unused = (x) && unused != (a) && unused != (b))
#define IFNOTIN3(x, a, b, c) \
if (unused = (x) && unused != (a) && unused != (b) && unused != (c))
#define IFNOTIN4(x, a, b, c, d) \
if (unused = (x) && unused != (a) && unused != (b) && unused != (c) && unused != (d))
#define IFNOTIN5(x, a, b, c, d, e) \
if (unused = (x) && unused != (a) && unused != (b) && unused != (c) && unused != (d) && unused != (e))
Вот пример работы с одним из указанных выше макросов:
#include <iostream>
#include "macros.h"
int main () {
std::cout << "Hello World\n";
for (int i = 0; i < 100; i ++) {
std::cout << i << ": ";
IFNOTIN4 (i, 7, 17, 32, 87) {
std::cout << "PASSED\n";
} else {
std::cout << "FAILED\n";
}
}
std::cin.get();
return 0;
}
Обратите внимание, что макросы должны отображаться в заголовочном файле, который включается везде, где вам нужно их использовать. Ваш код не сможет скомпилироваться, если вы уже используете переменную с именем "unused" в своем коде в другом месте или пытаетесь использовать эти макросы для сравнения чего-то другого, кроме целого. Я уверен, что вы можете расширять макросы для обработки других типов данных, если это становится необходимым.
Порядок операций сохраняется, используя скобки вокруг всех входов, и значение сохраняется в переменной перед сравнением, чтобы предотвратить множественное выполнение кода с интенсивным использованием ЦП.