C++11 searching a vector of objects - c++

I have 2 vectors
std::vector<MyObj> v;
std::vector<MyObj2> z;
The objects in the vectors both contain an int that has an ID. I want to see if when looking through v it has a matching id in z
So I thought that I could use `std::find_if and a Lambda.
for (int i=0; i < _z.size(); i++)
{
MyObj2 _g = _z.at(i);
auto iter = std::find_if(v.begin(), v.end(), [this](MyObj o)
{
if (o.getID() == _g.getID())
{
std::cout << "we have a match" << std::endl;
}
else
{
std::cout << "we DO NOT have a match" << std::endl;
}
});
}
but I am getting an error that I dont understand.
43: Member function 'getID' not viable: 'this' argument has type 'const MyObj2', but function is not marked const
I dont understand what has to be marked const and why?
an I needing something like in my .hpp?:
MyObj2& operator= (const MyObj2&);
MyObj2& operator== (const MyObj2&);

From cppreference about find_if:
UnaryPredicate must meet the requirements of Predicate.
wich liks to the concept of Predicate:
The Predicate concept describes a function object that takes a single iterator argument that is dereferenced and used to return a value testable as a bool.
In other words, if an algorithm takes a Predicate pred and an iterator first, it should be able to test the iterator using the predicate via a construct like if (pred(*first)) {...}.
The function object pred shall not apply any non-constant function through the dereferenced iterator. This function object may be a pointer to function or an object of a type with an appropriate function call operator.
There are two requirements stated in that text:
Your predicate (i.e. the lambda) has to return something convertible to bool. Some output to cout is not sufficient.
Your predicate is not allowed to call nonconst functions on the argument (i.e. the MyObj)
However, your code shows lots of compile errors but nothing that relates to the error you stated in your question. This is because you did not provide an SSCCE:
You did not capture _g in the lambda expression
You get compile errors if the predicate does not return something convertible to bool (see requirements for the predicate above)
you mismatched z and _z
I do not get the errors you described, because in your example code you copy the values from the vectors instead of taking a const reference. Copying is fine of course, and you can apply any non-const functions to those copies. I have a compileable mini-example of your code here: http://ideone.com/o8tPED
However, this is not how it should be implemented:
You should take references instead of copies of the vector elements (I am almost sure you do that in reality)
To avoid the error you reported, you will then have to declare both getID functions const. You should do that regardless of the usage in the algorithm, because functions that don't change the object should always say so.

You need to declare MyObj2::getID() as const: The compiler says so quite clearly:
struct MyObj2 {
int getID() const { ... }
...
};
It seems, your got a member _g for which this is captured. The _g mentioned before in this example is not captured because the capture clause explicitly restricts the capture to this. If you used the _g from the scope, getID() wouldn't need to be const.

Related

C++ Template - passing const value of type T by reference

I have a function with a template parameter T and would like to pass a value of type const T by reference.
The C++ compiler throws an error, (kind of) understandably so. Hence I was wondering if there exists a way to do this in a safe and concise way?
I created a very small example that reflects the issue I am having in my project.
(in my project the issue appears in a constant member function of some class, but from my experiments the issue should be "faithfully" reflected in the example below by use of a constant variable of int instead for simplicity's sake).
I am aware that I could theoretically use a separate template parameter "cT", but that would be horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument ...
I also understand that I could simply refrain from using templates at all and just specify this for every type.
I was just wondering if what I am trying to achieve below can be done with templates.
Thanks and have a nice day! :)
template<typename T>
bool ContainsElement(std::list<T>& findList, const T& elem)
{
for (auto& entry : findList)
{
if (entry == elem)
return true;
}
return false;
}
int main()
{
std::list<int*> myList;
const int testConst = 6;
auto pointerToTestConst = &testConst;
ContainsElement(myList, pointerToTestConst); // compiler screams
}
The issue is in incompatibility between pointers:
pointerToTestConst is of type const int* - non-const pointer to const integer. Therefore T=const int*
myList is of type list<int*>, deducing T=int*.
Since those types are not the same, compilation fails. Rightfully so, because elem would allow changing testConst if T=int*.
The issue manifests regardles you passing elem by (const) reference or value.
horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument
So what? They get a compiler error about the comparison. It is no more unsafe than your code right now. I would argue that a generic ContainsElement should not care what you pass to is as long as it compares equal to some element in the list, it is a match.
Of course STL already offers this for std::find and std::ranges::find which also does not care and it is not called unsafe because of it.
To "normalize" the type for pointers and non-pointers, std::conditional may be used, finally your template still defines single T parameter:
#include <type_traits>
template<typename T>
bool ContainsElement(std::list<T>& findList,
const std::conditional_t<
std::is_pointer<T>::value,
std::add_pointer_t<
std::add_const_t<
std::remove_pointer_t<T>
>
>,
std::add_const_t<T>
>& elem)
{
for (auto& entry : findList)
{
if (entry == elem)
return true;
}
return false;
}
Above example uses helper types with postfix _t introduced in C++14, for C++11, use std::conditional::type.
It works for std::list<int> and std::list<int*>.
Of course in case of std::list<int*> it will check whether list contains exactly the same pointer address, not the pointed value.

Error: no matching member function for call to 'push_back'

Why am I getting the error at the last two lines? The goal is to find the object in a set, and modify its content.
using namespace std;
struct mystruct {
int id;
vector<int> y;
mystruct(const int id):id(id) {}
bool operator<(const mystruct& x) const { return id < x.id; }
bool operator==(const mystruct& x) const { return id == x.id; }
};
void test() {
std::set<mystruct> sx;
mystruct x(1);
x.y.push_back(1); x.y.push_back(2);
sx.insert(x);
//
set<mystruct>::iterator i = sx.find(1);
const mystruct* x1 = &(*i);
const mystruct x2 = *x1;
cout << &(i->y) << endl;
cout << &(x1->y) << endl;
cout << x2.id << endl;
x2.y.push_back(3);
i->y.push_back(4);
}
It seems like the iterator returns a constant object, and doesn't let me use push_back() to modify the vector y. How can I overcome this?
Error:
test.cpp:27:8: error: no matching member function for call to 'push_back'
x2.y.push_back(z);
~~~~~^~~~~~~~~
/opt/local/libexec/llvm-6.0/include/c++/v1/vector:688:36: note: candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
_LIBCPP_INLINE_VISIBILITY void push_back(const_reference __x);
^
/opt/local/libexec/llvm-6.0/include/c++/v1/vector:691:36: note: candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
_LIBCPP_INLINE_VISIBILITY void push_back(value_type&& __x);
^
Since x2 is declared with a const qualifier, i.e. const mystruct x2, C++ compiler considers only const-qualified member functions for all invocations on x2 and any of its members. In particular, it is looking for void push_back (const int& val) const member function to invoke. Obviously, there is no such function, because push_back must modify the container, so the compiler produces an error explaining exactly what's going on:
candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
The only way to address this in your code is to remove const qualifier from x2's declaration.
The reason why you cannot modify x2 is that it is declared const, as has been pointed out by #dasblinkenlight. #songyuanyao's comment is right as for accessed to the object referenced by the iterator, but doesn't quite answer the question because it does not say why set iterators only allow const access.
The reason for this is that, as you know, a std::set is an ordered container whose structure is determined by comparing entries to one another using (by default) operator <. This means that there is a container invariant such that if an item a precedes another item b in the std::set, it follows that !(b < a). I put it like this because this also holds for std::multiset. Since, in a set, duplicates are not allowed, it follows that, actually, if a precedes b, then a < b. If this invariant were to be violated, then any set operation that requires the set to be sorted, such as find or insert, will have unexpected (to be precise, undefined) behavior.
Therefore, a std::set will not allow you to change an item using an iterator, because you could unwittingly change members of the item in a way that affect its proper place in the set, breaking the invariant and thereby causing undefined behavior.
Unfortunately, the compiler is not smart enough to understand that your comparison function only evaluates certain members, in this case, only id. If compiler and language were capable of analyzing and expressing this, they might figure that while i->id should be a reference to const, i->m for any other member m could safely be a reference to non-const.
There are at least four possible solutions to your problem:
The easiest but ugliest solution is to mark the members that you need to modify as mutable. Note that you must yourself ensure that changing them does not affect the sort order.
Another fairly ugly solution is the deliberate use of const_cast as in const_cast<mystruct &>(*i) where i is an iterator. Again, never change a member that has an effect on the sort order in this way.
A more elegant solution that, however, has additional run time overhead, is to add a level of pointer indirection (e.g. using std::unique_ptr) to properties that you want to modify. Note, however, that if you were to use the pointee in your comparison function, you would still run the risk of breaking the set invariant, only now the compiler and library will no longer prevent you from doing so!
The only way that works even if you want to change members affecting the sort order is to make a copy of the item, modify the copy, erase the old item, and then re-insert the copy. This allows the container to insert the copy at a different position if necessary.
Final notes:
A hashed container such as std::unordered_set will have exactly the same issue, only in this case it is not the comparison function, but both the hash and equality functions that you have to consider.
For the given reasons, a std::map or std::unordered_map may be a better match for your problem domain, as, in this case, the library knows that the mapped_type is not used to determine container structure. Note also how the value_type of a std::map or std::unordered_map is std::pair<const key_type, mapped_type> instead of std::pair<key_type, mapped_type> exactly for the same reason that changing the key could break the container invariants.

Initializer_list as function parameter and for loop in function

I'm new here and this is my first question.
So, I have this function:
std::string join(string_initializer_list p_input) const {
std::string output;
for (auto const & s : p_input) {
output.append(s);
}
return output;
}
The main objective of that function is to join a list of strings returning a new string.
For the parameter I've used a std::initializer_list<std::string> aliased as string_initializer_list.
The parameter is passed by value because after doing a bit of research, I noticed that I was just passing pointers around and that this was the most idiomatic and correct way to do so.
My question has to do with the for loop: which is more correct in this case and why? Should I use auto const & or auto &&?
The thing is: I don't want to change any of the strings from the initializer_list, and I want to leave that clear. From that perspective const & seems more correct but I would to know your opinions about this matter.
After doing some research, testing it in C++ and reading lots of questions here, I don't yet fully understand how auto && works.
Also, in a first draft I also had the parameter being passed as && as a way to prevent it from being an lvalue.
I intend only to use this function for quick stuff like this:
join({"Hello, ", "world"});
In rare ocasions, one of those std::strings will end up being an lvalue:
join({"Hello, ", some_string});
The main concept here is to make a cheap and easy way of concatenating strings. This is another function I made, similar with the one above but for a different context.
std::string & concatenate(std::string & p_output, string_initializer_list p_input) const {
for (auto const & s : p_input) {
p_output.append(s);
}
return p_output;
}
Once more, the perfectionist in me is asking about that for loop.
The iterator type of std::initializer_list<T> is const T*. So when you dereference it, you get an lvalue of type const T. It does not matter whether you use const auto& or auto&& to iterate since either way you will get an lvalue reference to const T. I would prefer const auto& here for readability---auto&& often suggests you're working with template code in which the thing could be either an lvalue or an rvalue.

std::partition error when partitioning a vector of a type of object

Am using the following line of code to partition a number of objects through the following code:
vector<PrimitiveBvhRepresentation>::iterator lastFirst = std::partition(first, last, &Engine::BVHNode::MiddlePointPartition);
the partition functions makes use of the following function to determine if the respective objects in the vector are true or false.
bool MiddlePointPartition(PrimitiveBvhRepresentation current)
{
if (splittingAxis == 0)
{
return current.boundingBoxCentroid.x <= splittingAxisValue;
}
else if (splittingAxis == 1)
{
return current.boundingBoxCentroid.y <= splittingAxisValue;
}
else
{
return current.boundingBoxCentroid.z <= splittingAxisValue;
}
}
yet upon execution i get the following error:
Error 63 error C2064: term does not evaluate to a function taking 1 arguments
When clicking on the error it takes me to the algorithm class. Is the reference to the function written wrong?
Non static member functions are not functions.
Try this:
auto lastFirst = std::partition(first, last,
[this](auto const& x){return MiddlePointPartition(x);}
);
in C++14. In C++11 replace the auto const& with PrimitiveBvhRepresentation const&.
In C++03, consider a new compiler, or use std::bind(&mem_fun, this) as the 3rd argument to partition instead of the lambda.
The standard algorithms require a function object that can be used with the normal function call syntax, so a pointer to member doesn't qualify. If you need a non-static member function, wrap it with std::mem_fn.
std::partition requires a unary predicate, but your member function, assuming that it is non-static, takes two arguments (the implicit this parameter, and current). It's not clear to me whether you'd want this function to be a static member, or just operate on this.

std::initializer_list<> and a Reference Parameter

I'm new to using initializer lists and I'm wondering if they work similar to other stl containers. By that I mean do they copy values? What I'm trying to do is a simple min() function like this:
template <class T> T& minArgs(const std::initializer_list<T&>& Arguments)
{
const T* Smallest = Arguments.begin();
for (const T* I = begin(Arguments); I != end(Arguments); ++I)
{
if (*I < *Smallest) Smallest = I;
}
return *Smallest;
}
However when I call the function I get this from GCC:
error: 'const' qualifiers cannot be applied to 'int&'
I've been playing around with this and it seems initializer_lists may not do what I want; I want the function to except non-POD arguments as well. Would a va_list be a better alternative?
Thanks!
When I try it, I get these errors. Yet, when I get rid of your pointless use of references, it all works.
std::initializer_list stores values, not references. You should be taking a const std::initializer_list<T> &, not a const std::initializer_list<T&> &.
All I'm trying to do is write a function that takes any number of arguments, by reference, and returns a reference to the largest of them.
[...]
Is this possible with initializer_lists?
No. std::initializer_list is for values, not references. But I see no reason why you couldn't take the items by value instead of by reference. Or, more to the point, why don't you just use std::min, which can take an initializer list?