OS: Windows 10 Pro 64-bit(10.0, build 18362)
IDE: Visual Studio 2019(Version 16.4.3) [Before this I was using VS 2010]
Language: c++ and VC++
Here's my simple code in which i just want to find element 3 and print it if found
std::vector<int> intVect;
for (int counter = 0; counter < 5; counter++)
{
intVect.push_back(counter);
}
std::find(intVect.begin(), intVect.end(), [](int a)
{
if (a == 3)
{
std::cout << "Item Found.." << std::endl;
}
});
The probelm is when i compile this code it's giving me error like below:
Error C2678 binary '==' : no operator found which takes a left-hand
operand of type 'int' (or there is no acceptable conversion)
c:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\algorithm 41
You can do this easily without a lambda:
auto val = std::find(intVect.begin(), intVect.end(), 3);
if (val != intVect.end()) {
std::cout << "Value found\n";
}
However, it's also possible to use a lambda, but you need to use std::find_if (available in the same header as std::find: <algorithm>):
auto val = std::find_if(intVect.begin(), intVect.end(), [](int i) { return i == 3; });
if (val != intVect.end()) {
std::cout << "Value found\n";
}
But there's really no point. You'd only use find_if in this case if you had something more complex, like a struct and you were searching for a particular member:
struct complex {
int id;
...
};
std::vector<complex> things = ...;
auto val = std::find_if(things.begin(), things.end(), [](const complex& c) { return c.id == 3; });
if (val != things.end()) {
std::cout << "Value found\n";
}
std::find expects a value to be compared to elements, so it should be
if (std::find(intVect.begin(), intVect.end(), 3) != intVect.end())
{
std::cout << "Item Found.." << std::endl;
}
If you want to use a lambda as predicate, you should use std::find_if; note that the lambda should return bool.
if (std::find_if(intVect.begin(), intVect.end(), [](int a) { return (a == 3); }) != intVect.end())
{
std::cout << "Item Found.." << std::endl;
}
Related
I am trying to use std::partition to partition a vector into multiple part based on whitespace.
void solution2()
{
std::vector<string> v{ "10","20","","30","40","50","","60","70" };
auto i = begin(v);
while (i != end(v)-1)
{
auto it = std::partition(i, end(v)-1, [](auto empty) {return empty != ""; });
std::copy(i, it, std::ostream_iterator<int>(std::cout, " "));
i = it;
}
}
For example in the above code I want to partition it into multiple and condition to partition is whitespace ""
so the vector v should partition to 3 groups
"10" "20"
"30" "40" "50"
"60" "70"
The problem I am facing is in line
auto it = std::partition(begin(v), end(v)-1, [](string empty) {return empty != ""; });
Error
Severity Code Description Project File Line Suppression State
Error C2679 binary '=': no operator found which takes a right-hand operand of type 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' (or there is no acceptable conversion) C:\source\repos\out\build\x64-debug\sample C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\xutility 3919
Any suggestion what can be changed to fix the error.
Any other efficient way to do the same using std::range or std::view
With C++20, this can be done trivially with ranges::split_view:
std::vector<std::string> v{ "10","20","","30","40","50","","60","70" };
for(auto part : v | std::views::split(""))
{
for(auto num : part) std::cout << num << ',';
std::cout << '\n';
}
Demo
Using std::find to get the next empty string would be more appropriate here.
auto i = begin(v), e = end(v);
while (i != e) {
auto it = std::find(i, e, "");
std::copy(i, it, std::ostream_iterator<std::string>(std::cout, " "));
std::cout << '\n';
if (it == e) break;
i = it + 1;
}
I am getting a syntax error on the line
if (auto result = ranges::find_if(height.begi with a red squiggly line under find_if:
no instance of overloaded function matches the argument list
auto is_gt_or_eq = [height, i](int x) { height[x] >= height[i]; };
if (auto result = ranges::find_if(height.begin(), height.end(), is_gt_or_eq); result != height.end()) {
std::cout << "First larger or equal to element in height: " << *result << '\n';
}
else {
std::cout << "No larger or equal to element in height\n";
}
I found this similar code on cppreference, and it does run:
auto is_even = [](int x) { return x % 2 == 0; };
if (auto result = ranges::find_if(height.begin(), height.end(), is_even); result != height.end()) {
std::cout << "First even element in height: " << *result << '\n';
}
else {
std::cout << "No even elements in height\n";
}
I believe the error is in this line of code:
auto is_gt_or_eq = [height, i](int x) { height[x] >= height[i]; };
To start with, you forgot the return keyword for the return statement. So your lambda is returning void by default, thus not a valid predicate. The library won't allow you to call its function due to this mismatch.
Beyond that, x is (a copy of) the element of height, not an index of the container. There is no need to access the container again for the element in the lambda. So, the simplest fix is
auto is_gt_or_eq = [height, i](int x) { return x >= height[i]; };
There's also no need to constantly re-access height[i] in the lambda. It's not a bad idea to just capture that value instead.
auto is_gt_or_eq = [hi = height[i]](int x) { return x >= hi; };
Your lambda is now smaller, more inline-able and (to me at least) more readable.
i'm new to C++. My program is a quiz game which user can choose category and level for the questions. At first, i use the struct data type
struct QuestionInfo
{
string category;
string level;
string question;
string answer;
};
then
vector<QuestionInfo> vec;
The idea of this part is to store the info of the question include (category, level, question and answer) to each element.
Then after building menu and the output questions UI, i go to the filters
void category_filter()
{
for (unsigned int i = 0; i < vec.size(); i ++)
{
if (category_choice != vec[i].category)
vec.erase(vec.begin() + i );
}
}
Void level_filter()
{
for (unsigned int i = 0; i < vec.size(); i ++)
{
if (level_choice != vec[i].level)
vec.erase(vec.begin() + i );
}
}
So the idea of the filters is to delete the elements which not contain the matched category and level. But the output questions did not match with the category and the level i had choose before. I'm not sure what I'm doing wrong.
Let me explain you the problem with my example. Suppose you have a vector of 10 elements, valid indexes are 0 till 9 elements. You have to erase 5th element i == 4. You erase it, then 6th element with index 5 moves to place of 5th elements with index 4. After that you increase i in for, it becomes 5. Thus you skip previous 6th element, that is now 5th with index 4.
You may fix your code like below, moving i ++ to the condition.
for (unsigned int i = 0; i < vec.size(); ) {
if (category_choice != vec[i].category)
vec.erase(vec.begin() + i );
else
i ++;
}
The preferable solution in C++ way is demonstrated by #Jonathan.
You're getting tripped up by not accounting for the indexing shift that occurs when you erase an element. I personally would rely on remove_if and erase with a lambda to accomplish this:
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return category_choice != i.category; }, end(vec));
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return level_choice != i.level; }, end(vec));
Alternatively you might consider combining them for a bit of speed improvement:
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return category_choice != i.category || level_choice != i.level; }, end(vec));
You might want to remove_if + erase:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
struct QuestionInfo
{
std::string category;
std::string level;
std::string question;
std::string answer;
QuestionInfo(std::string category, std::string level, std::string question, std::string answer) :
category(category), level(level), question(question), answer(answer) {}
};
std::vector<QuestionInfo> vec;
std::string category_choice = "cat1";
std::string level_choice = "lev1";
vec.push_back(QuestionInfo("cat1", "lev1", "q1", "a1"));
vec.push_back(QuestionInfo("cat1", "lev2", "q2", "a2"));
vec.push_back(QuestionInfo("cat2", "lev1", "q3", "a3"));
vec.push_back(QuestionInfo("cat2", "lev2", "q4", "a4"));
std::cout << "\nNot filered" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
auto filter_category = std::remove_if(vec.begin(), vec.end(), [&](auto const &info) {return category_choice != info.category; });
vec.erase(filter_category, vec.end());
std::cout << "\nFilered by category" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
auto filter_level = std::remove_if(vec.begin(), vec.end(), [&](auto const &info) {return level_choice != info.level; });
vec.erase(filter_level, vec.end());
std::cout << "\nFiltered by level" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
system("pause");
return 0;
}
As mentioned by others, the remove_if + erase is a standard and expressive way to achieve what you want. But you may also consider non-destructive filtering with a copy_if into a new container, or even without using any additional storage with Boost.Range adaptor boost::adaptors::filtered or boost::filter_iterator. Look here for examples.
Lambda with function bodies that contain anything other than a single
return statement that do not specify a return type return void.
via 《C++ Primer》 5th Edition, Page 389.
However, if we write the seemingly equivalent program using an if
statement, our code won't compile:
//error: can't deduce the return type for the lambda.
transform(vi.begin(), vi.end(), vi.begin(), [](int i) { if(i < 0) return -i; else return i; } );
via 《C++ Primer》 5th Edition, Page 396.
I write a program in Visual Studio:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main(void) {
vector<int> vi{ 1, -2, 3, -4, 5, -6 };
/* Is the return type void? */
transform(vi.begin(), vi.end(), vi.begin(), [](int i) {
if (i < 0) return -i;
else return i; });
for (int i : vi)
cout << i << " ";
cout << endl;
system("pause");
return 0;
}
But it can correctly run.
And then, I add some statements in Visual Studio:
auto f = [](int i) {
if (i < 0) return -i;
else return i; };
As I move the cursor to the f, it shows me that the f's return type is int.
Why is this?
I am confused.
C++ Primer 5th Edition covers C++11, and in C++11, the statement you quoted is true. However, C++14 supports deducing return types in more situations, including when a lambda has multiple return statements, as long as they all return the same type. Presumably your version of Visual Studio supports C++14, or at least this feature of it.
I am having issues with the following piece of code while using threads.
I read on the Microsoft site that appending to the concurrent_vector does not mess with iterators, so I did not provide and mutex for the duration of the find_if operation.
So the error I am receiving is an "Access violation"
I have 6 threads running concurrently. Should I wrap this in a mutex? Does it need one. I'm fairly new to C++.
std::stringstream key;
key << "SearchString " << ID << ", " << "Options" << ", " << Date;
auto &it = std::find_if(
m_spList.begin(), m_spList.end(),
[&key] (std::unique_ptr<IBaseObject>const &bo){
return bo->ID() == key.str();
}
);
if (it != m_spList.end()) {
while (it != m_spList.end()) {
ReplacePartResult* rpr = dynamic_cast<ReplacePartResult*>(it->get());
if (rpr) {
if (rpr->ReplaceItem) {
replaceBOMID = rpr->BOMID > 0 ? rpr->BOMID : 0;
if (_parentPart) {
_parentPart->TemplateBomID = rpr->BOMID;
_parentPart->Name = rpr->Name;
_parentPart->Description = rpr->Description;
}
}
}
it = std::find_if(
++it, m_spList.end(),
[&key](std::unique_ptr<IBaseObject>const &bo){
return bo->ID() == key.str();
}
);
}
}
Not 100% why, but i re-factored the find_if into a new function and explicitly defined my iterator type and it seems to be behaving. Maybe sening the stringstream into the lambda was the issue?
concurrency::concurrent_vector<std::unique_ptr<IBaseObject>>::iterator IBaseObject_FindKey(concurrency::concurrent_vector<std::unique_ptr<IBaseObject>>& mv, std::string const& _key)
{
return std::find_if(std::begin(mv), std::end(mv), [&_key](std::unique_ptr<IBaseObject>const &bo){return bo->ID() == _key; });
}