Why would candidate '=' operator overload be marked as const? - c++

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());

Related

C++ std::vector<std::pair<const int, int>> can not insert element

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.

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.

How exactly do I use the functions push_back and pop_back()? I looked them up in the following liks but still don't understand

http://www.cplusplus.com/reference/vector/vector/push_back/ (C++11 Version)
What is the difference and/or advantages of void push_back (const value_type& val); & void push_back (value_type&& val) and which do you suggest I use?;
I don't understand how to fill in the arguments (const value_type& val) & (value_type&& val)
I don't understand the second sentence under the parameter section. (It's a bit too wordy for me to get). I do understand what val is though
It doesn't give an example I can understand real well. Can I get other examples using vectors or some video links that explain the use of the function in practice better?
http://www.cplusplus.com/reference/vector/vector/pop_back/
It doesn't give an example I can understand real well. Can I get other examples using vectors or some video links that explain the use of the function in practice better?
If you are a beginner, just read over the additional qualifiers like const, & and &&. The methods in the STL are implemented in a way, that they behave consistent over all overloads:
I will give you a small example here:
std::vector<int> myvector;
myvector.push_back(5);
int five = 5;
myvector.push_back(five);
Now the more in depth part of the answer:
First (const value_type& val). The & character signals, that we take the argument by reference, that means we don't copy the argument, but get a fancy pointer, that will behave like the object itself.
You may not want, that your variable is changed, if you push it back to a vector. To get a promise, by the programmer of the STL, that he will not change your variable while pushing it back to the vector, he can add the const before the type.
The reason it is implemented that way, is that it may prevent an unneeded copy. (First copy the argument onto the stack to call push_back and the second time copy it at the position in the vector. The first copy is unnecessary and saved by the const reference.)
This is all nice and simple, but there are cases, where the compiler is not allowed to take a reference of a value and pass it to a function. In case of temporary values, there is no reference to take, because there is no variable in memory. Take the following line for example.
myvector.push_back(5);
Since the 5 has no address, it can't be passed as a reference. The compiler can not use the first overload of the function. But the programmer also does not want to waste the time for the copy onto the stack. That is why C++11 added new semantic. A so called rvalue for such temporary objects. If you want to write a function to take such an rvalue, you can do so by using type&& rvalue_variable. The value in this case the 5 is moved onto the stack by using the move constructor of the type. For trivial types like int, this will be the same as the copy constructor. For complex types like std::vector there are shortcuts one can take if one is allowed to rip the temporary object apart. In case of the vector, it does not need to copy all the data in the vector to a new location, but can use the pointer of the old vector in the new object.
Now we can look at the example again:
std::vector<int> myvector;
myvector.push_back(5); // push_back(const int&) can't be applied. The compiler chooses push_back(int&&) for us
int five = 5;
myvector.push_back(five); // push_back(const int&) can be applied and is used by the compiler
// The resulting vector after this has the two values [5, 5]
// and we see, that we don't need to care about it.
This should show you how you can use both of them.
push_back():
std::vector<int> vec = { 0, 1, 2 };
vec.push_back(3);
pop_back():
vec.pop_back();
vec.pop_back();
If you need more clarification:
push_back(const T& val) adds its parameter to the end of the vector, effectively increasing the size by 1 iff the vector capacity will be exceeded by its size.
pop_back() doesn't take any parameters and removes the last element of the vector, effectively reducing the size by 1.
Update:
I'm trying to tackle your questions one by one, if there is anything unclear, let me know.
What is the difference and/or advantages of void push_back (const value_type& val); & void push_back (value_type&& val) and which do you suggest I use?;
Prior to C++11, rvalue-references didn't exist. That's why push_back was implemented as vector.push_back(const value_type& val). If you have a compiler that supports C++11 or later, std::vector.push_back() will be overloaded for const lvalue references and rvalue references.
I don't understand how to fill in the arguments (const value_type& val) & (value_type&& val)
You as a programmer do NOT choose how you pass arguments to push_back(), the compiler does it for you automagically, in most cases.
I don't understand the second sentence under the parameter section. (It's a bit too wordy for me to get). I do understand what val is though
value_type is equal to the type of vector that you declared. If a vector is declared with std::string, then it can only hold std::string.
std::vector<std::string> vec;
vec.push_back("str"); // Ok. "str" is allowed.
vec.push_back(12); // Compile-time error. 12 is not allowed.
What is the difference and/or advantages of void push_back (const value_type& val); & void push_back (value_type&& val) and which do you suggest I use?
void push_back(const value_type&) takes an argument, that is then copied into the vector. This means that a new element is initialized as a copy of the passed argument, as defined by an appropriate allocator.
void push_back(value_type&&) takes an argument, that is then moved into the container (this type of expressions are called rvalue expressions).
The usage of either of two depends on the results you want to achieve.
I don't understand how to fill in the arguments (const value_type& val) & (value_type&& val)
In most cases you shouldn't think about which version to use, as compiler will take care of this for you. Second version will be called for any rvalue argument and the first one for the rest. In a rare case when you want to ensure the second overload is called you can use std::move to explicitly convert the argument expression into xvalue (which is a kind of rvalues).
I don't understand the second sentence under the parameter section. (It's a bit too wordy for me to get). I do understand what val is though
The sentence in question is:
Member type value_type is the type of the elements in the container, defined in vector as an alias of its first template parameter (T).
This means that value_type is the same type as the type of vector's elements. E.g., if you have vector<int> then value_type is the same as int and for vector<string> the value_type is string.
Because vector is not an ordinary type, but a template, you must specify a type parameters (which goes into angle brackets <> after vector) when defining a variable. Inside the vector template specification this type parameter T is then aliased with value_type:
typedef T value_type;
It doesn't give an example I can understand real well. Can I get other examples using vectors or some video links that explain the use of the function in practice better?
The main thing you need to remember is that vector behaves like a simple array, but with dynamicly changeable size and some additional information, like its length. push_back is simply a function that adds a new element at the end of this pseudo-array. There is, of course, a lot of subtle details, but they are inconsequential in most of the cases.
The basic usage is like this:
vector<int> v; // v is empty
v.push_back(1); // v now contains one element
vector<float> v2 { 1.0, 2.0 }; // v2 is now a vector with two elements
float f = v2.pop_back(); // v2 now has one element, and f is now equals 2.0
The best way to understand how it works is to try using it yourself.

Overload resolution of Eigen's operator()() when wrapping it with Boost.Python

I am in the process of wrapping a C++ library using Boost.Python. Some of the functions therein return Eigen::MatrixXd objects (a dynamically sized double-precision matrix class). On the Python side I just need to access the dimensions of the matrices, this is easy, and retrieve some matrix elements with Eigen's overloaded operator()() method. Unfortunately there are 4 such overloaded methods, one has to pick the right one manually, i.e. giving Boost.Python a function pointer typedef with the correct signature, something along the lines
namespace bpy = boost::python;
bpy::class_<Eigen::MatrixXd>("MatrixXd",
"Variable-size double-precision matrix class",
bpy::init<const Eigen::MatrixXd&>()
)
.def("__call__", static_cast<parop_signature>(&Eigen::MatrixXd::operator()))
// ...
;
The problem is that I cannot figure out what the correct signature of the function is. "Operationally" it should take two integer indices and return a double value. However,
typedef const double& (Eigen::MatrixXd::*parop_signature)(int, int) const;
results in the following compilation error (Mac OS X, clang++ in C++11 mode, Boost.Python V1.61):
address of overloaded function 'operator()' cannot be static_cast to type
'const double &(MatrixXd::*)(int, int) const'
...static_cast<const double& (Eigen::MatrixXd::*)(int, int) const>(&Eigen::MatrixXd::operator())
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:111:41: note:
candidate function
EIGEN_STRONG_INLINE CoeffReturnType operator()(Index row, Index col) const
^
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:171:5: note:
candidate function
operator()(Index index) const
^
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:334:5: note:
candidate function
operator()(Index row, Index col)
^
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:392:5: note:
candidate function
operator()(Index index)
Fair enough, you'd say: but I cannot figure out how I can tell Boost.Python that CoeffReturnType is actually a double here (or maybe a const double&, who knows?), and that IndexType will resolve to a plain int at the end of the day. I have tried all sorts of combinations of typedef-s with or without const qualifiers.
Even tried to declare a C++11 -style function pointer like
auto eigen_indexfn = std::mem_fn<double(int,int)>(&Eigen::MatrixXd::operator());
, no success, I get
candidate template ignored: couldn't infer template argument '_Tp'
mem_fn(_Rp _Tp::* __pm)
^
Is there someone who has gone through this already and can provide me with the correct signature of what for all intents and purposes should be as simple as "double Eigen::MatrixXd::operator(int, int)" ? Any hints would be much appreciated.
It seems the error origins from the fact that Eigen::Index is not int but defaults to ptrdiff_t. Just because int can implicitly be cast to Eigen::Index does not mean that you can cast a function pointer which requires Eigen::Index to a function pointer which requires int. If that was possible, you would end up passing integers of the wrong size over the stack.
Addendum: If you really prefer int over ptrdiff_t, you can define EIGEN_DEFAULT_DENSE_INDEX_TYPE to int before including Eigen, as documented here, be aware that this will break ABI compatibility.
Many thanks to #xao and #chtz for their help. For reference (pun intended) I show the solution here that finally worked, with comments.
Part one, the signature of Eigen's parenthesis operator for accessing matrix elements:
// const element access in dynamically-sized double matrices
// note that the Index type is ptrdiff_t
// and the CoeffReturnType is const double&
typedef const double& (Eigen::MatrixXd::*parop_signature)(ptrdiff_t,ptrdiff_t) const;
Part two, an appropriate return policy must be defined. We want to use the const double& returned by operator()() so the policy will be copy_const_reference:
bpy::class_<Eigen::MatrixXd>("MatrixXd",
"Variable-size double-precision matrix class",
bpy::init<const Eigen::MatrixXd&>()
)
.def("__call__", static_cast<parop_signature>(&Eigen::MatrixXd::operator()),
bpy::return_value_policy<bpy::copy_const_reference>())
After going through all this it compiles and can be invoked from Python correctly.

why std::map<int, float> cant use operator[]: error C2678?

Having:
std::map<const int, float> m_areaCost;
I'm trying to compile the following:
inline float getAreaCost(const int i) const {
return m_areaCost[i];
}
Which leads to the following error:
error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const std::map<_Kty,_Ty>' (or there is no acceptable conversion)
I used to think when we call [elementId] we get element value or default element value so I wonder how can such simple case lead to compilation errors?
Presumably m_areaCost is a member of the object that getAreaCost is a member of. However, getAreaCost is marked as a const member function. This means it can't cause any modification to members. So the m_areaCost member is const in this function.
You cannot call operator[] on a const std::map because its effect is that it inserts a new element if it does not yet exist. Instead use std::map::at:
return m_areaCost.at(i);
Because you state that your function is const, and the [] is not a const operator.
As you said so yourself, the [] !creates or returns a new value.. if it cannot create then you cannot use this operator.. I would use find() and then return the .second value if it is there, something like:
auto it = m_areaCost.find(i);
if (it != m_areaCost.end()) return (*it).second
Because you marked the method to be const.
When you do return m_areaCost[i]; you can actually create the entry in the map if the key does not exist, therefore the operation is not constant so you have a mismatch between your constant function and non-constant operator[] of the map.
If you want the member function to be const then you have to use find to find the entry for the key.
The reason is that getAreaCost being declared as const, like others has stated, but I'd like to give some more suggestions:
A function should always return a useful value, so I would suggest getAreaCost to be:
inline float getAreaCost(int i) const {
std::map<const int, float>::const_iterator It = m_areaCost.find(i);
if (It != m_areaCost.end()) return It->second;
return 0.0f;
}
Some points:
1. input parameter is pass-by-value, so no need forconst int i, just use int i.
2. if you find things like std::map<const int, float>::const_iterator is verbose, give it a typedef. (If you dont, feel free to keep write it, of course :P)
3. 0.0f is just for example, you can return any default value that is suitable in your case.