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.
Related
I can't use std::set_union because I'm not overloading the assignment operator correctly.
I'm using a std::set of my own struct, NodeChange_t, which itself contains another struct, point_t. Here are these guys' operator overloads:
// ---- defs.h
struct point_t
{
double x;
double y;
...
void operator=(const point_t &p)
{
x = p.x;
y = p.y;
}
...
};
struct NodeChange_t
{
SNode node;
point_t change;
ListDigraph *graph;
...
void operator=(const NodeChange_t &otherChange)
{
this->node = otherChange.node;
this->change = otherChange.change;
this->graph = otherChange.graph;
}
...
};
// ---- _2DSurface.cpp
//Problematic code:
void _2DSurface::updateInnerSurfaceV2(_2DSurface &outerSurf, std::set<NodeChange_t> *nodeChanges)
{
std::set<NodeChange_t> additions;
...
// add stuff to additions
std::set_union(additions.begin(), additions.end(), nodeChanges->begin(), nodeChanges->end(), nodeChanges->begin());
...
}
In this case, I want *nodeChanges to be overwritten. But the error I keep getting is:
src/_2DSurface.cpp:308:7: note: in instantiation of function template specialization
'std::__1::set_union<std::__1::__tree_const_iterator<ct, std::__1::__tree_node<ct, void *> *, long>,
std::__1::__tree_const_iterator<ct, std::__1::__tree_node<ct, void *> *, long>, std::__1::__tree_const_iterator<ct,
std::__1::__tree_node<ct, void *> *, long> >' requested here
std::set_union(nodeChanges->begin(), nodeChanges->end(), additions.begin(), additions.end(), nodeChanges.begin());
include/defs.hpp:258:7: note: candidate function not viable: 'this' argument has type 'const std::__1::__tree_const_iterator<ct,
std::__1::__tree_node<ct, void *> *, long>::value_type' (aka 'const ct'), but method is not marked const
void operator=(struct ct &otherChange)
How does it even make sense that an assignment operator would be marked const, if the whole point is to modify what's on the left hand side? I've been messing around with the const qualifier but can't seem to get anywhere. Any help is appreciated.
How does it even make sense that an assignment operator would be marked const, if the whole point is to modify what's on the left hand side?
The assignment operator is not marked const. In fact, the error message says as much; it is one of the triggers for the error. Take another look at the relevant parts of the error message with some key emphasis:
candidate function not viable: 'this' argument has type [snipped] (aka 'const ct'), but method is not marked const
The other trigger for the error is that the object receiving the assignment is marked const. If you look around the types mentioned in the error message, you might notice "const_iterator". This is your clue! Somewhere a constant iterator is being de-referenced and a value assigned to the result. All iterators involved are set iterators, so let's take a look at documentation for set. A set's iterator type is a constant iterator; you cannot write to it. (For a set, the iterator and const_iterator types are usually aliases for the same type. This redundancy allows an interface that is consistent with other containers.)
The set_union algorithm requires an output iterator for the destination. A set does not have an output iterator. So even though the word "set" appears in "set_union", set_union cannot directly output a set.
Also concerning is another detail from the set_union documentation: "The resulting range cannot overlap with either of the input ranges." You cannot accomplish what you want (replace one of the inputs with the union) in one step with set_union. If that is the tool you want to use, you'll need to output the union to an auxiliary container, then update nodeChanges in a separate step. Hoewever, it would probably be simpler to use set::insert (variation 5):
nodeChanges->insert(additions.begin(), additions.end());
int main() {
using kv = std::pair<const int ,int>;
std::vector<kv> vec;
kv a{1,1};
vec.insert(vec.begin(),a);
}
I tried to insert element into that vector, but the compiler gives this eerror:
cannot assign to non-static data member 'first' with const-qualified type 'const int'
while push_back() will compile correctly.Why is that? And what's the correct way to insert element into such vector?
ADD:
The reason why I use std::pair<const int, int> is that I want to implement something like std::map, and the key in the key-value pair should not be modified. But I'm not sure if std::pair<const int, int> is the correct approach to do this.
Let's start by observing the following:
std::pair<const int, int> a, b;
a=b;
This will not compile either. Why? Because you are in effect declaring the following class:
class my_pair {
public:
// ... Bunch of irrelevant stuff
const int first;
int second;
};
This class has its default assignment operator deleted. The default assignment operator is equivalent to assigning class members individually:
pair<first,second> &operator=(const pair<first, second> &other)
{
first=other.first;
second=other.second;
return *this;
}
This is a rough example of what a default assignment operator looks like.
But you can't modify the first class member, here, because it is a const class member. Hence, the default assignment operator is deleted. End result: cannot assign a std::pair with a const value to another such pair.
You should now be able to figure out by yourself why insert() on your vector of pairs doesn't work.
insert() is, roughly, equivalent to shifting over all existing values by one, in the vector, after the insertion point, to make room for the new value. In other words:
value[n+1]=values[n];
For all values past the insertion point (I'm ignoring for the moment the work done to grow the vector by one value, which is irrelevant to this discussion).
But the default assignment operator, as I explained, is deleted because the value has a const class member.
push_back works because nothing needs to be inserted, only a new value needs to be added to the end of the vector. If the vector needs to be reallocated, to accomodate its growth, that uses copy or move constructors, which are still valid for a std::pair with a const value.
Due to the const nature of the your data type kv (it has a const member first), it's copy-assignment operator has been implicitly deleted by the compiler.
Specifically, the overload of std::vector::insert you're using requires the data type (kv) be CopyAssignable (can be copied with a form of a = b). Your const member prohibits that.
On the other hand, std::vector::push_back requires the type be CopyInsertable (it can be copy-constructed), which your type is compliant with.
Therefore, insert will not work, but push_back will. The easiest fix is to lose the const from the kv first member. If you don't want someone to modify a key/value, then don't let them. When returning key/value tuples do so as const pointers or references.
My example code is
class A
{
int a = 0;
public:
void setA(const int value)
{
a = value;
}
};
std::map<std::string, std::set<A>> Map{{"A", {}}};
Map.rbegin()->second.rbegin()->setA(2);
I get the following error: "Member function 'setA' not viable: 'this' argument has type 'const A', but function is not marked const"
My question is why rbegin() returns a const pointer to A? Or why is the std:pair's second a const in the std::map?
All std::set elements are exposed in const fashion. That's because they're both keys and values, and if you could modify keys willy-nilly then you'd ruin the tree structure inside the set.
It is currently not possible to directly modify set elements. You will have to remove then re-insert.
(This has nothing to do with the encapsulating map.)
Basically, rbegin() returns a reverse iterator which points to an object of type A which is stored in the std::set in a const manner.
The reason behind such behaviour is quite simple: it is necessary to protect std::set from inadvertent changes of elements which are stored inside.
You should remember, that std::set stores its elements in a tree-like data structure to ensure fast search/insert/remove operations. Possible changes of elements inside std::set might lead to wrong elements comparison and corruption of data structure, that is why all iterators return by begin()/end() methods and their analogues expose elements in a const fashion.
Given the following code:
#include <set>
struct X {
int a, b;
friend bool operator<(X const& lhs, X const& rhs) {
return lhs.a < rhs.a;
}
};
int main() {
std::set<X> xs;
// some insertion...
auto it = xs.find({0, 0}); // assume it != xs.end()
const_cast<X&>(*it).b = 4; // (1)
}
Is (1) defined behavior? I.e., am I allowed to const_cast a reference to an element obtain from a const_iterator of a std::set and modify it if the modification does not alter the ordering?
I have read some posts here and there proposing this kind of const_cast, but I was wondering if this was actually defined behavior.
Whether this has defined behaviour is unclear, but I believe it does.
There appears to be no specific prohibition in the description of std::set on modifying the values, other than the restriction you already hinted at that the comparer must return the same result when passed the same inputs ([associative.reqmts]p3).
However, the general rule on modifying objects defined as const does apply. Whether set defines its elements as const is not spelt out. If it does, then modifying the elements is not allowed ([dcl.type.cv]p4).
But [container.requirements.general]p3 reads:
For the components affected by this subclause that declare an allocator_type, objects stored in these components shall be constructed using the allocator_traits<allocator_type>::construct function and
destroyed using the allocator_traits<allocator_type>::destroy function (20.7.8.2).
std::set<T> declares an allocator_type, which defaults to std::allocator<T>. std::allocator_traits<allocator_type>::construct passes it a T *, causing a T to be constructed, not a const T.
I believe this means std::set<T> is not permitted to define its elements as const, and for lack of any other prohibition, means modifying the elements through const_cast is allowed.
Personally, if I couldn't find any better alternative, I would consider avoiding the issue by putting the whole thing in a wrapper struct, which defines a member mutable X x;. This would allow modifications without const_cast, so long as you take care to avoid changing the relative order of elements already in the set. It would additionally serve as documentation to other readers of your code that the elements of your set can and will be modified.
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.