This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C++ comparing bunch of values with a given one
I'm needing to check for equality in a for loop in C++, however the loop needs to work for x equaling multiple possibilities.
For example, right now I have something similar to:
if(x==a || x==b || x==c || x==d || x==e || x==f || x==g || x==h)
{
//loop body
}
But with the number I have, it looks messy and I was wondering if there was a shorthand way of saying "if (x == (any of these))" or if writing them all out was the only option.
Thanks!
Thank you for your question, now as I found a solution (and an elegant one I dare say), I'll use it myself.
Unlike solutions with std::find : Will a ) be unrolled to N comparisons in compile time b) work with any types which X can be compared to
struct TagAnyOf {};
template <typename... Args>
std::tuple <TagAnyOf, Args...> AnyOf (Args&&... args)
{
return std::tuple <TagAnyOf, Args...> (TagAnyOf(), std::forward<Args>(args)...);
}
template <class X, class Tuple, size_t Index, size_t ReverseIndex>
struct CompareToTuple
{
static bool compare (const X& x, const Tuple& tuple)
{
return x == std::get<Index> (tuple) || CompareToTuple<X, Tuple, Index+1, ReverseIndex-1>::compare (x, tuple);
}
};
template <class X, class Tuple, size_t Index>
struct CompareToTuple <X, Tuple, Index, 0>
{
static bool compare (const X& x, const Tuple& tuple)
{
return false;
}
};
template <typename X, typename... Args>
bool operator == (const X& x, const std::tuple<TagAnyOf, Args...>& any)
{
typedef std::tuple <TagAnyOf, Args...> any_of_type;
return CompareToTuple <X, any_of_type, 1, std::tuple_size<any_of_type>::value-1>::compare (x, any);
}
Usage
int main()
{
int x = 1;
if (x == AnyOf (1, 2, 3, 4))
{
std::cout << "Yes!" << std::endl;
}
else
{
std::cout << "No!" << std::endl;
}
if (x == AnyOf (4, 3, 2, 1))
{
std::cout << "Yes!" << std::endl;
}
else
{
std::cout << "No!" << std::endl;
}
if (x == AnyOf (2, 3, 4, 5))
{
std::cout << "Yes!" << std::endl;
}
else
{
std::cout << "No!" << std::endl;
}
return 0;
}
Consider using a function that takes an initializer_list (this is a c++11 feature).
the first parameter would be the left hand value(x in your case), and the rest of the parameters will be right hand values.
Here is an example that accomplishes the task using templates.
#include <iostream>
#include <cstdlib>
#include <algorithm>
template<class T>
bool Test(T const& test, std::initializer_list<T> const& values){
return std::find(std::begin(values), std::end(values), test) != std::end(values);
}
int main(){
char var1 = 'a';
char var2 = 'a';
char var3 = 'b';
char var4 = 'c';
char var5 = 'd';
if (Test<char>(var1,{var2,var3,var4,'o',var5})){
std::cout << "true. at least one is equivelent" << std::endl;
}else{
std::cout << "false. none are equivelent" << std::endl;
}
if (Test<char>(var1,{var3,var4,var5})){
std::cout << "true. at least one is equivelent" << std::endl;
}else{
std::cout << "false. none are equivelent" << std::endl;
}
return EXIT_SUCCESS;
}
If you are doing this with classes, make sure you overload the '!=' operator.
edit: mistake fixed. pointed out by GManNickG
I was wondering if there was a shorthand way of saying "if (x == (any
of these))"
Yes, the standard gives you std::find and std::find_if to answer exactly this question:
int a=3,b=5,c=6,d=7;
std::array<int,4> vals{{a,b,c,d}}; // or std::vector
int x=5;
bool foundit= (end(vals) != std::find_if(begin(vals), end(vals),x );
You will need
#include <array>
#include <algorithm>
You can also use std::initializer_list<int> booleans{a,b,c,d}; instead of vector or array
If your conditions where more complicated, you could use find_if:
bool foundit= (end(vals) !=
std::find_if(begin(vals), end(vals),
[&x](const int &v){return v*(v+x)<x;}));
Related
I want to check whether an element exists in the vector or not. I know the below piece of code will check it.
#include <algorithm>
if ( std::find(vector.begin(), vector.end(), item) != vector.end() )
std::cout << "found";
else
std::cout << "not found";
But I have the vector of any type. i.e. std::vector<std::any>
I am pushing elements into vector like this.
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
So I need to find whether string "A" present in the vector or not. Can std::find help here?
As of now I am using below piece of code to do this
bool isItemPresentInAnyVector(std::vector<std::any> items, std::any item)
{
for (const auto& it : items)
{
if (it.type() == typeid(std::string) && item.type() == typeid(std::string))
{
std::string strVecItem = std::any_cast<std::string>(it);
std::string strItem = std::any_cast<std::string>(item);
if (strVecItem.compare(strItem) == 0)
return true;
}
else if (it.type() == typeid(int) && item.type() == typeid(int))
{
int iVecItem = std::any_cast<int>(it);
int iItem = std::any_cast<int>(item);
if (iVecItem == iItem)
return true;
}
else if (it.type() == typeid(float) && item.type() == typeid(float))
{
float fVecItem = std::any_cast<float>(it);
float fItem = std::any_cast<float>(item);
if (fVecItem == fItem)
return true;
}
}
return false;
}
This should work good I guess:
#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>
int main(){
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
int i = 10;//you can use any type for i variable and it should work fine
//std::string i = "A";
auto found = std::find_if(temp.begin(), temp.end(), [i](const auto &a){
return typeid(i) == a.type() && std::any_cast<decltype(i)>(a) == i;
} );
std::cout << std::any_cast<decltype(i)>(*found);
}
Or to make the code a bit more generic and reusable:
#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>
auto any_compare = [](const auto &i){
return [i] (const auto &val){
return typeid(i) == val.type() && std::any_cast<decltype(i)>(val) == i;
};
};
int main(){
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
//int i = 10;
std::string i = "A";
auto found = std::find_if(temp.begin(), temp.end(), any_compare(i));
std::cout << std::any_cast<decltype(i)>(*found);
}
Live demo
Important note: this is guaranteed to work only within single translation unit due to stadard requirements on std::any type (for example same types don't need to have same type identifier in different translation units)
Using an any for this kind of purpose is not a good use of any. The best way to go is just to use a variant - since you have a closed set of types:
struct Equals {
template <typename T>
constexpr bool operator()(T const& a, T const& b) const { return a == b; }
template <typename T, typename U>
constexpr bool operator()(T const& a, U const& b) const { return false; }
};
using V = std::variant<int, float, std::string>
bool isItemPresentInAnyVector(std::vector<V> const& items, V const& item)
{
auto it = std::find_if(items.begin(), items.end(), [&](V const& elem){
return std::visit(Equals{}, elem, item);
});
return it != items.end();
}
Actually it's even better, because as Kilian points out, variant's operator== already works exactly like this:
using V = std::variant<int, float, std::string>
bool isItemPresentInAnyVector(std::vector<V> const& items, V const& item)
{
return std::find(items.begin(), items.end(), item) != items.end();
}
Unfortunately if you want to find an std::any instance in a vector of std::any instances the answer is no.
std::any does need some "magic" for example to be able to handle the creation of unknown object types but this machinery is private and must only supports object creation and not equality comparison.
It would be possible to implement what you are looking for using the same approach, but not with standard std::any that doesn't publish the needed details. The "manager" template needs to enumerate all possible operations and, for example, in g++ implementation they're "access", "get_type_info", "clone", "destroy", "xfer".
variant is completely different, because explicitly lists all the allowed types and therefore in any place it's used can access all the methods.
Comparison with typeId() should be avoided since it's dependent from translation unit.
A much safer approach can be used with any_cast of pointers:
template<typename T>
std::optional<T> find(const std::vector<std::any>& v)
{
for(auto&& e : v){
if(auto ptr = std::any_cast<T>(&e)){
return *ptr;
}
}
return std::nullopt;
}
Find first element with the given type, or nullopt if it's not found.
If we want to find all element with a specific instead:
template<typename T>
std::vector<T> findAll(const std::vector<std::any>& v)
{
std::vector<T> out;
for(auto&& e : v){
if(auto ptr = std::any_cast<T>(&e)){
out.push_back(*ptr);
}
}
return out;
}
Usage:
int main()
{
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
temp.emplace_back(12);
temp.emplace_back(std::string("B"));
auto outInt = findAll<int>(temp);
std::cout << "out int: " << outInt.size() << std::endl;
for(auto&& out : outInt)
std::cout << out << std::endl;
auto outString = findAll<std::string>(temp);
std::cout << "out string: " << outString.size() << std::endl;
for(auto&& out : outString)
std::cout << out << std::endl;
auto singleInt = find<int>(temp);
if(singleInt)
std::cout << "first int " << *singleInt << std::endl;
auto singleBool = find<bool>(temp);
if(!singleBool)
std::cout << "ok: bool not found" << std::endl;
}
LIVE DEMO
If the types are int, float and string (or a limited set of types), you can use a combination of std::variant and std::get_if to achieve what you want to do in a simple manner:
std::get_if is to determine which of the types is stored in the std::variant.
A minimal example:
#include <iostream>
#include <vector>
#include <string>
#include <variant>
int main(){
std::vector<std::variant<int, float, std::string>> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
for (const auto& var: temp) {
if(std::get_if<std::string>(&var)) {
if(std::get<std::string>(var) == "A") std::cout << "found string\n";
}
if(std::get_if<int>(&var)) {
if(std::get<int>(var) == 10) std::cout << "found int\n";
}
if(std::get_if<float>(&var)) {
if(std::get<float>(var) == 3.14f) std::cout << "found float\n";
}
}
}
Live Demo
A few times in my program, I've had to check if a variable was one of many options. For example
if (num = (<1 or 2 or 3>)) { DO STUFF }
I've messed around with 'OR's, but nothing seems to be right. I've tried
if (num == (1 || 2 || 3))
but it does nothing.
I'd like to conveniently distinguish between several groups. For example
if (num = (1,2,3))
else if (num = (4,5,6))
else if (num = (7,8,9))
Here's a way in C++11, using std::initializer_list:
#include <algorithm>
#include <initializer_list>
template <typename T>
bool is_in(const T& v, std::initializer_list<T> lst)
{
return std::find(std::begin(lst), std::end(lst), v) != std::end(lst);
}
with that, you can do:
if (is_in(num, {1, 2, 3})) { DO STUFF }
It is not very efficient though when not used with built-in types. int will work fine, but if you compare std::string variables for example, the produced code is just awful.
In C++17 however, you can instead use a much more efficient solution that works well with any type:
template<typename First, typename ... T>
bool is_in(First &&first, T && ... t)
{
return ((first == t) || ...);
}
// ...
// s1, s2, s3, s4 are strings.
if (is_in(s1, s2, s3, s4)) // ...
The C++11 version would be very inefficient here, while this version should produce the same code as hand-written comparisons.
If the values you want to check are sufficiently small, you could create a bit mask of the values that you seek and then check for that bit to be set.
Suppose, you care about a couple of groups.
static const unsigned values_group_1 = (1 << 1) | (1 << 2) | (1 << 3);
static const unsigned values_group_2 = (1 << 4) | (1 << 5) | (1 << 6);
static const unsigned values_group_3 = (1 << 7) | (1 << 8) | (1 << 9);
if ((1 << value_to_check) & values_group_1) {
// You found a match for group 1
}
if ((1 << value_to_check) & values_group_2) {
// You found a match for group 2
}
if ((1 << value_to_check) & values_group_3) {
// You found a match for group 3
}
This approach works best for values that don't exceed the natural size your CPU likes to work with. This would typically be 64 in modern times, but may vary depending upon the specifics of your environment.
You have to do the comparison with each value. E.g.
if (num == 1 || num == 2 || num == 3) { stuff }
You may also want to consider a switch and intentionally falling through cases (although I don't think it's the best solution for what you're stating).
switch (num) {
case 1:
case 2:
case 3:
{DO STUFF}
break;
default:
//do nothing.
}
I just had a similar problem and I came to these C++11 solutions:
template <class T>
struct Is
{
T d_;
bool in(T a) {
return a == d_;
}
template <class Arg, class... Args>
bool in(Arg a, Args... args) {
return in(a) || in(args...);
}
};
template <class T>
Is<T> is(T d) {
return Is<T>{d};
}
Or as alternative without the recursion terminating method. Be aware that here the order of comparisons is undefined and that this does not terminate early if the first match is found. But the code is more compact.
template <class T>
struct Is {
const T d_;
template <class... Args>
bool in(Args... args) {
bool r{ false };
[&r](...){}(( (r = r || d_ == args), 1)...);
return r;
}
};
template <class T>
Is<T> is(T d) {
return Is<T>{d};
}
So for both solutions the code would look like:
if (is(num).in(1,2,3)) {
// do whatever needs to be done
}
You can define a set of integers, add the desired values to it, and then use the find method to see if the value in question is in the set
std::set<int> values;
// add the desired values to your set...
if (values.find(target) != values.end())
...
I needed to do something similar for enums. I have a variable and wish to test it against a ranges of values.
Here I've used a variadic template function. Note the specialisation for const char* type, so that is_in( my_str, "a", "b", "c") has the expected outcome for when my_str stores "a".
#include <cstring>
template<typename T>
constexpr bool is_in(T t, T v) {
return t == v;
}
template<>
constexpr bool is_in(const char* t, const char* v) {
return std::strcmp(t,v);
}
template<typename T, typename... Args>
constexpr bool is_in(T t, T v, Args... args) {
return t==v || is_in(t,args...);
}
Example usage:
enum class day
{
mon, tues, wed, thur, fri, sat, sun
};
bool is_weekend(day d)
{
return is_in(d, day::sat, day::sun);
}
float n;
if (n<1) exit(0);
if (n / 3 <= 1)
// within 1, 2, 3
else if (n / 3 <= 2)
// within 4, 5, 6
else if (n / 3 <= 3)
// within 7, 8, 9
I am using C++ for particle physics and I find myself commondly writing lines like this:
bool isItCorrect(int i){
if(i==11 || i == 62 || i==-11 || i == 11002 || i==22002) return True
else return false;
}
What is the easiest way for me to make this shorter in C++. In python I could do:
def isItCorrect( i ):
if (i is in [11,62,-11,11002,22002]) return True
else return False
You can use variadic templates in C++ 11 and define:
template <typename T, typename T1>
bool isItCorrect(T t, T1 t1) {
return t == t1;
}
template <typename T, typename T1, typename... T2>
bool isItCorrect(T t, T1 t1, T2... t2) {
return t == t1 || isItCorrect(t, t2...);
}
and use:
bool isItCorrect(int i) {
return isItCorrect(i, 62, -11, 110022, 22002);
}
It may not be the "simplest", but I generally just use a switch, eg:
bool isItCorrect(int i)
{
switch (i)
{
case 11:
case 62:
case -11:
case 11002:
case 22002:
return true;
default:
return false;
}
}
std::set provides a count function which works much like the is in from python.
bool isItCorrect(int i) {
return std::set<int>({11, 62, -11, 110022, 22002}).count(i);
}
You might be able to use something like this: You can wrap a std vector into a struct and then write a function that takes the value to check against the field. This will work as is if you know all the elements of the field a head of time.
#include <iostream>
#include <vector>
struct field {
std::vector<int> data { 11, 62, -11, 11002, 22002 };
};
bool isItCorrect( int i, const field& f ) {
for ( auto& d : f.data )
if ( d == i ) return true;
return false;
}
int main() {
field f;
std::cout << std::boolalpha;
std::cout << isItCorrect( 2, f ) << std::endl;
std::cout << isItCorrect( 62, f ) << std::endl;
std::cout << "\nPress any key to quit.";
std::cin.get();
return 0;
}
Output
false
true
Press any key to quit.
If you are working with arbitrary types, and you don't know how many elements to check against you can template it as such using variadic function templates and parameter packs:
fieldT.h
#ifndef FIELD_T_H
#define FIELD_T_H
#include <vector>
#include <type_traits>
template<class T>
class fieldT {
private:
std::vector<T> _data;
public:
explicit fieldT( std::vector<T>& data ) : _data( data ) {}
template<class... Args>
fieldT( Args&&... args ) :
_data { std::forward<Args>( args )... } {}
std::vector<T> data() {
return _data;
}
};
template<class T>
bool isItCorrectT( T t, fieldT<T>& f );
template<class T, class... Args>
bool isItCorrectT( T t, Args... args );
#include "fieldT.inl"
#endif // FIELD_T_H
fieldT.inl
template<class T>
bool isItCorrectT( T t, fieldT<T>& f ) {
for ( auto& d : f.data() )
if ( d == t ) return true;
return false;
}
template<class T, class... Args>
bool isItCorrectT( T t, Args... args ) {
fieldT<T> f( args... );
for ( auto& d : f.data() )
if ( d == t ) return true;
return false;
}
Here I turned the class into a class template and then I also template the function isItCorrect only I created 2 overloads. One that will accept the value to check against and the fieldT<T> object where the 2nd overload will accept the value to check against and any arbitrary amount of arguments for that type.
Here is a demo of using the above class template using both overloaded methods
for each constructor of the class:
#include <iostream>
#include <vector>
#include "fieldT.h"
int main() {
std::cout << std::boolalpha;
// First create a vector of floats and instantiate a field<float> passing in that vector.
std::vector<float> floats { 1.0f, 1.1f, 1.2f, 1.3f, 1.4f };
fieldT<float> ff( floats );
// checking for both true and false case using the fieldT<T> above
std::cout << isItCorrectT( 2.0f, ff ) << std::endl;
std::cout << isItCorrectT( 1.2f, ff ) << std::endl;
// Or we can pass the values directly into the function as the
// overloaded version will take all but the first parameter
// and create a field<T> object populating its internal vector.
std::cout << isItCorrectT( 1.5f, 2.9f, 7.5f, 3.4f ) << std::endl;
std::cout << isItCorrectT( 1.5f, 2.9f, -3.7f, 1.5f, 8.9f ) << std::endl;
std::cout << "\nPress any key to quit.";
std::cin.get();
return 0;
}
Output
false
true
false
true
Press any key to quit.
What is real nice about this is that it remains generic and portable and can be used for any arbitrary type <T> with any amount of arguments of <T>.
I did not add in any tests to make sure that the values after the first are of type <T> but one should be able to easily assert that.
As you can see from within the main. The actual function call is very clean, easy to read and use. Even the class and the functions that operate on it are very clean and readable, easy to follow and understand and most important they are generic and portable, reusable.
bool isItCorrect(int i)
{
set<int> correctNums { 11, 62, -11, 11002, 22002 };
return correctNums.find(i) != correctNums.end();
}
I believe it's the simplest way.
I was trying to make a function that assigns y to x regardless whether x, y are int or std::string. I wrote this code:
#include <iostream>
#include <string>
#include <typeinfo>
template <typename T>
T& assign(T& x, T& y){
if ( typeid(x).name() == "Ss" && typeid(y).name() == "Ss" ){
std::string k = static_cast<std::string>(y);
x = k;
return x;
}
else if ( typeid(x).name() == "i" && typeid(y).name() == "i" ){
int k = static_cast<int>(y);
x = k;
return x;
}
else{
std::cout << "uncorrect assignment" << std::endl;
}
}
int main(){
std::string a = "empty_string";
std::string b = "Hi there";
assign(a, b);
std::cout << a << std::endl;
}
But it doesn’t work.
It gives the error:
[Error] invalid static_cast from type ‘std::basic_string<char>’ to type
at line 14:
int k = static_cast<int>(y);
I can’t understand, what is the problem?
I know the objection: I might have just defined function assign as:
template <typename T>
T& assign(T& x, T& y){
x = y;
}
which works. However, I was working on an other more complex function on which I have to (or at least I haven’t found any way other than) use static_cast.
So, if you could, please, explain to me what is the mistake in this example, I may try to fix the function I am working on.
Thank you very much,
Simone.
To do what do you want, you need C++17 and if constexpr. And the use of something that works compile-time, not of typeid that works runtime.
The problem is that with your code, typeid permit, runtime, to choose the if or the else part of your code, but the compiler must compile both part. So must compile
int k = static_cast<int>(y);
x = k;
when T is std::string. This give an error.
You need a type-traits (std::is_same, by example), that is evaluated compile-time, and a construct that avoid the compilation of the wrong part. This construct is if constexpr ( <test> ) (where the <test> is valuable compile time) but, unfortunately, is available only from C++17.
So, in C++17 you can write
template <typename T>
void assign (T & x, T const & y)
{
if constexpr ( std::is_same<T, std::string>::value ) {
std::string k = static_cast<std::string>(y);
x = k;
}
else if constexpr ( std::is_same<T, int>::value ) {
int k = static_cast<int>(y);
x = k;
}
else {
std::cout << "uncorrect assignment" << std::endl;
}
}
but, pre C++17, you have to follows different ways.
To handle different types separately inside a function, an option is to define a local struct with overloaded function call operators to different types:
#include <iostream>
#include <string>
template<typename T>
T& assign(T& x, const T& y) {
struct {
void operator()(std::string& lhs, const std::string& rhs) {
std::cout << "Type is std::string." << std::endl;
lhs = rhs;
}
void operator()(int& lhs, const int& rhs) {
std::cout << "Type is int." << std::endl;
lhs = rhs;
}
} assign_impl;
assign_impl(x, y);
return x;
}
int main() {
/* Test No. 1 */ {
std::string dest, src = "Foo";
std::cout << "Test result: " << assign(dest, src) << std::endl;
}
/* Test No. 2 */ {
int dest, src = 32;
std::cout << "Test result: " << assign(dest, src) << std::endl;
}
}
The code above will work on C++98 and above but its disadvantage is that it will raise compiler errors if you try to use it with unhandled types.
I have code that uses fold expressions to compare function argument against integer parmeters of class template.
Code works AFAIK, but I wonder if it is possible to do what I want without _impl helper function.
Full code(my question is if contains can be implemented without contains_impl):
#include <algorithm>
#include <iostream>
#include <utility>
#include <cstdlib>
#include <tuple>
template <int H, int... T>
class if_set {
private:
template<typename... Ts>
bool contains_impl(const int& val, Ts... ts) const{
return (false || ... || (val == ts));
}
public:
bool contains(const int& val) const {
return contains_impl( val, H, T...);
}
};
using namespace std;
int main()
{
constexpr if_set<1,3,4,5> isi;
for (int i = -1; i < 10; ++i) {
cout << i << ": " << boolalpha << isi.contains(i) << endl;
}
if_set<'a','e','i','o','u', 'A', 'E', 'I', 'O', 'U'> vowels;
string word = "ARCADE FIRE: Modern man";
cout << word << endl;
word.erase(remove_if(word.begin(), word.end(), [&vowels](const char& c){return vowels.contains (c);}), word.end());
cout << word << endl;
}
Note 1: I know this code has many issues, I do not plan to use it in production and I discourage people from using it directly or as inspiration, this is a toy example I wanted to implement after reading interesting article about frozen C++ library.
Note 2: false || looks ugly, but IDK any nicer way.
Yes, you can do it:
template <int H, int... T>
class if_set {
public:
bool contains(const int& val) const {
return ((val == H) || ... || (val == T));
}
};
Alternatively, you could just work on std::integer_sequences:
template<typename T1, typename T2, T1... Is>
bool contains(std::integer_sequence<T1, Is...>, T2 val) {
return (... || (val == Is)); // perhaps should be (false || ... || (val == Is)), but this appears to work
}