Const placement to stop editing of pointer data with boost shared_ptr - c++

So Wikipedia tells me (correctly i believe) that to stop the editing of the data of a pointer and the pointer itself that I should do this:
void function(int const * const var)
Is this the same as this function:
void function(const int * const var)
And in that case why is it allowed? Because I know that you cant do this because of duplicate const compile error:
void function(const int const * const var)
I essentially want to do the same with a boost pointer. Would I do this:
void function(const boost::shared_ptr<int> const var)
And how would this affect my ability to loop over say a shared pointer to a vector? Could I do this with that guard:
void function(const boost::shared_ptr<std::vector<int>> const var)
{
for (unsigned int i = 0; i < var->size(); ++i)
{
std::cout << var[i];
}
}
Adition: After Brian's answer
So if I create a a pointer like this:
boost::shared_ptr<vector<int>> lala
And i use it in this function:
function (const boost::shared_ptr<std::vector<const int>> var)
will that work?

Yes, int const * const var is the same as const int * const var. You can put const before or after the type it modifies, for "historical reasons". See http://www.stroustrup.com/bs_faq2.html#constplacement
For a smart pointer object, you indeed cannot do
const boost::shared_ptr<int> const
because both consts modify the same type. Instead, you want
const boost::shared_ptr<const int>
The first const prevents the pointer itself from being modified (i.e., reassigned to point to another int) and the const in the template parameter tells the object's operator* to return a const int&, which prevents modification of the int pointed to.
This does not prevent iteration over a vector or other container in the manner you have described, for the same reason why you can still iterate over a const vector normally.
Edit in response to question edit:
This works:
void f(const boost::shared_ptr<const std::vector<int> > var);
// ...
boost::shared_ptr<vector<int> > lala;
f(lala);
The first const in the parameter doesn't affect parameter passing at all; it only tells the function itself not to modify the parameter. The reason why we can add a const in the template parameter is that a boost::shared_ptr<T> can be initialized from boost:shared_ptr<U> where T and U are not necessarily the same type, as long as U* is implicitly convertible to T*. If T is the same as U except with greater cv-qualification, as in this case, the conversion is possible.
Don't do std::vector<const int>. I'm fairly sure that's not legal. (At least I've gotten multiple screens of compilation errors every time I've tried it.)

const before or after the int is the same so:
int const * var
and
const int * var
are the same and mean the value pointed to cannot be changed
const after the * means that the pointer cannot be reassigned.
If I understand correctly, you'd like to make the vector const. If that's the case the syntax would be this:
void function(const boost::shared_ptr<const std::vector<int>>& var)
The smart pointer is passed by const reference because it's cheaper than passing the smart pointer by value and has the same effect. The object pointed to by the smart pointer is immutable by declaring the type it points to as const.

You've correctly reasoned that const shared_ptr<Foo> doesn't make Foo a const object. This "loophole" is described on wikipedia. Instead, you need to change the pointer type stored by boost::shared_ptr. This can be done in the template argument itself:
void function(const boost::shared_ptr<const std::vector<int>>& var)
boost::shared_ptr has a copy-constructor that allows for a const-type to be copied from a non-const-type. The opposite should not be possible.

Before, or after?
The below two lines are semantically equivalent, both declare a pointer which value cannot be changed, that refers to an int that cannot be changed.
int const * const p1 = ...;
const int * const p2 = ...;
The const keyword binds to whatever is directly to the left, unless there's nothing to the left, in which case it will hug whatever is on the right.
The more const, the better?
typedef boost::shared_ptr<int> shared_int_ptr;
const shared_int_ptr const p3; // ill-formed
The above typedef is provided to make it easier to see that boost::shared_ptr<int> is a single name, and therefore we cannot add const on both sides; duplicate consts are (as you mentioned) not legal C++.
Just applying one wouldn't be sufficient either, since that would make the wrapper const, but not the internal object that it is referring to (the int).
Previously we wrote that boost::shared_ptr should wrap around an int, but since we want to make the wrapped type const, well.. let's wrap the shared_ptr around what we want:
void func (const boost::shared_ptr<const int> foo);
In the above func is not able to modify foo, nor the int referred to by foo.

Related

How are templates work so that the classes are properly generated by the compiler to work with both pointer types and non pointer types?

Given
int i = 42;
int* p = &i;
we cannot do
const int * & d = p;
In my current understanding, this is because const int * & d can be read as "d is a reference of a pointer to an int that is a const" and p is "a pointer to an int", which means the RHS needs to be turned into a temporary being "a pointer to an int that is a const" and that cannot be binded directly with a reference (as on the LHS). We would need to have, say, const int* const& d = p;. Possible references: 1, 2.
That said, consider the following toy code:
template<typename T>
class X
{
public:
void fun(const T& d) { }
};
At first, I thought if I do
X<int*> x;
x.fun(p);
The method generated by the compiler would be void fun(const int*& d), and I would get an error due to the above reason.
However, there is no error and it works "as expected" (by that I mean, we always have d itself as the const no matter T is replaced by pointer types or by non-pointer types).
My current guess is that, for the above scenario, the method generated by the compiler is something like void fun(int* const& d).
May I ask if my guess is correct? How are templates work so that the classes generated by the compiler work with pointers "as expected"?
Your guess is correct.
For const T, const is qualified on type T directly. Given T is int* (non-const pointer to non-const int), const T results in const pointer, i.e. int* const (const pointer to non-const int) but not const int* (non-const pointer to const int).

Why are const used so frequently in C++?

Recently i was looking for a solution to sort only the values of map and then i found this code
template <typename T1, typename T2>
struct less_second {
typedef pair<T1, T2> type;
bool operator ()(type const& a, type const& b) const {
return a.second < b.second;
}
};
map<string, int> mymap;
// …
vector<pair<string, int> > mapcopy(mymap.begin(), mymap.end());
sort(mapcopy.begin(), mapcopy.end(), less_second<string, int>());
The first thing that stuck my mind was why in many sample code and solutions I find people frequently using the const keyword is it a good practice to do that, if so why?
Why use const?
My take is that you should try to use const as much as you can. Mutable values are always a source of errors. You can avoid these errors by avoiding mutable values.
Function of const in C++
const is used a lot because it has a lot off different meanings:
If we look at this signature, we can see two types of const bool operator ()(type const& a, type const& b) const {...}: There is a const reference const &a and const &b, which just means that you cannot modify a or b, which you passed as reference.
There is also the const at the end of the method, which means that this method does not modify the instance. Figure a get-method. This method will not modify the instance it gets the value from. You can signal this to the caller by using T get_element(int at_index) const {...}.
There are further const pointers.
Here you could change the value in the address, but not the address itself:
int *const a = new int;
*a = 1; // legal
a = &var; // illegal
Here you can change the address the pointer is pointing to, but not the value in that address.
const int *a = new int;
*a = 1; // illegal
a = &var; // legal
(Hope I did not confuse the two)
Have a pointer that does not allow any change:
const int * const a = new int;
*a = 1; // illegal
a = &var; // illegal
Further reading:
When to use constant pointers instead of const references.
YT - Const in C++
There is furthermore constexpr, which can calculate a value at compile time. This is different than the const keyword, and might confuse you at the beginning.
The const in the parameters of this function allows the function to be called with const values (in the case of int a value like 1 is const). And also since the parameters are a reference, the const guarantees that the original variables will not be changed.
The const after the member function declaration, guarentees that an object of that class is not modified when that function is called.
So by looking at the definition of the function, we can automatically know the guarentees and what type of variables we can use.
Also knowing these assumptions, the compiler can create better code.

Does const keyword go before or after Type?

I wrote something like this:
#define Parent C*
class C{
public:
Parent parent;
C():parent(NULL){}
};
void fun(Parent &p){
//condition..return;
fun(p->parent);
}
Something wired happened when I was trying to make the reference parameter constant to avoid any unintended changes to the referenced object.
first:
I added const befor Parent, like this: void fun(const Parent &p). But it does not compile on this line fun(p->parent);.
error message is:
Invalid arguments ' Candidates are: void fun(const C * &) '
then:
I changed the position of const like this: void fun(Parent const &p) and all of a sudden, the problem was gone.
Why??? What's the difference?
How does const work? It is always applied to what comes right before it. However, when there is nothing before, it applies to what comes right after it. In other words, const Type& is the same as Type const&.
Now, do not ever use preprocessor macros to define type aliases. There are two ways of doing that in C++: using and typedef. The fact you used a macro is the reason why you observe this peculiar behaviour. The code that fails to compile is expanded to the following:
void fun(const C*& p); // The pointee is const, but the pointer itself is not
I suppose you are then doing things on the object pointed by p... But this object is const! In the second example, the expansion results in:
void fun(C* const& p); // The pointee is not const, but the pointer itself is
Then, you are doing stuff on the pointee but this time it's the pointer, not the pointee, that is const, so everything is fine. If you had used an alias or a typedef, it would have gone perfectly fine in both cases: see this example.
As a rule of thumb, you should always put const on the right of the thing you want it to be applied to. This would allow you to read types the way they are meant to be read: from right to left.
const works from right to left, making the previous part const,
int * const p; // makes constant pointer to int
except for const type and type const at the beginning, which are the same:
int const * p; // pointer to const int
const int * p; // ditto
so if parent is e.g. struct Parent{}, then Parent const & p and const Parent & p should be the same; if however Parent is a macro, e.g. C*, they are different.

Constness of template member in const member function

How is const applied to a template member in a const member function? I found the following to be interesting (this is in VS15):
class TcpSocket;
class TcpThread
{
TcpSocket* Listener() const;
std::vector< TcpSocket* > sockets_;
};
TcpSocket* TcpThread::Listener() const
{
auto s = sockets_.front();
return s;
}
I added the auto to clarify what was going on. It is deduced as TcpSocket*, so the non-const version of front is being selected. However, if I insert
sockets_.erase(sockets_.begin());
as the first line of code, it fails to compile, essentially saying that sockets_ is const.
It makes sense for it to work as it does, but there is evidently more going on here than simply "treat each member as const in a const member function.
sockets_ inside Listener is const. Let's have a look at what front returns:
reference front();
const_reference front() const;
So we'll get a const_reference, in this case a TcpSocket * const&.
This is where your expectation is incorrect. Stripping away the reference for sake of clarity, you expect a const TcpSocket*, it gives you a TcpSocket * const. The former is a pointer to a const TcpSocket, the latter is a const pointer to a TcpSocket.
So what front gives you is a pointer which you can't change to a TcpSocket which you can change.
As such, it's perfectly valid to make a non-const copy of this pointer with its pointee available for modification:
auto s = sockets_.front();
//sockets_.front() returns TcpSocket* const
//s copies it to a TcpSocket*
It's not that the non-const version of front is called, it's just that you're storing pointers, and then you're putting it into auto which always deduces by-value (and not by reference--for which you need auto& =). Because you're copying the const pointer, you then have your own copy, and so const is omitted for it, unless you explicitly define it that way. That's why you're deducing TcpSocket* instead of TcpSocket* const.
If you want to verify this, try doing auto& s = _sockets.front() and see what type you get then.
Note. too, that since you're storing a pointer, that vector::const_reference you'd get back would be point to a const pointer and not a pointer to const.
The container itself, being const in that scope, means that you can't change its sequence of elements, or what they point to. So you can't say _sockets.erase() nor can you say _sockets[0]. However, since the elements themselves are pointers to a non-const TcpSocket, that means that you can do pretty much whatever you want with them. It's the container you can't fiddle with.
Even though std::vector< TcpSocket* > sockets_; is itself const, the containee (i.e. TcpSocket*) is not const.
That's why you get the non-const deduction.
Why you do not get TcpSocket * const:
Although front() returns TcpSocket* const &, auto is deduced to be TcpSocket*.
Consider:
double const & foo();
// ...
double const a = 4.0;
auto b = a; // valid, decltype(b) === double
double c = a; // valid, too
double d = foo(); // valid
auto e = foo(); // decltype(e) === double
You're making a copy anyway, so why would that copy be const?
You do not get anything valueable from having s being TcpSocket * const.
Why you do not get TcpSocket const *:
That's just because you store TcpSocket* in the vector. The constness of front() makes sure that the stored pointer is not altered (i.e. you cannot make sockets_.front() point to another TcpSocket from within Listener()) but maintining const-correctness of the object pointed to is due to the user.

equal_range and 2 overloads have no legal conversion for 'this' pointer

Here is this simple code
#include <map>
class MyMap : public std::multimap<int*, int*>
{
public:
void foo(const int* bar) const
{
equal_range(bar);
}
};
int main()
{
MyMap myMap;
int number;
myMap.foo(&number);
return 0;
}
It doesn't compile, and give the following error
error C2663: 'std::_Tree<_Traits>::equal_range' : 2 overloads have no legal conversion for 'this' pointer
I have seen many topic about this error, and it seems that it is a const issue. It compiles fine if I turn foo(const int* bar) into foo(int* bar).
Problem is, I don't see how foo content is supposed to change anything to my MyMap object. std::multimap proposes a const version of equal_range:
http://www.cplusplus.com/reference/map/multimap/equal_range/
What is my problem?
Thank you
Check the definition of equal_range:
pair<const_iterator,const_iterator> equal_range (const key_type& k) const;
It expects a constant reference to key_type: const key_type& k.
What you were trying to supply was a pointer to a constant integer: const int* bar
Why doesn't this work even though both values are const?
A constant reference to an integer const int& foo means that you cannot let foo refer to another integer, but it is allowed to change the value of the referenced integer.
A pointer to a constant integer const int* foo means that you can let foo point to another integer, but you cannot change the value of the integer it points to.
What the map actually expects is a const int*& k, but the map will automatically convert this if you supply a int* only (without the const).
[Edit]
Also note that a MyMap object still cannot be changed by your foo function even if you change const int* to int* as there still is another const at the end of your foo function. This const at the very end declares the function as constant meaning that it cannot modify the current object in which it is executed. If it was trying to modify it or call anything that could potentially modify it, you would get a compiler error. (Disclaimer: There are ways to modify a class from within a const function anyway but that's another topic.)
If fact the right compiler message can give you the answer. For one of the two overloads (the const one):
/usr/local/include/c++/v1/map:1836:41: note: candidate function not viable: 1st argument ('const int *')
would lose const qualifier
const int* is a pointer to a const int. The method expects a const key_type&, i.e. const int*& argument, that is a const reference to a (non-const) int*.
When confused, prefer to write const int* as int const*, which is the same thing. Then you'll see the difference to const int*& more clearly.
For your code to work, use int* instead of const int*.
I believe that the problem has to do with the mismatch on the key_type.
On one hand you have a multimap where key_type=int* while on the other hand you are passing a key_type=const int* thus attempting to drop the const qualifier on the key_type. I was confused by this too because I was expanding the key_type in my mind to get const int*& which should be compatible. However, the mismatch happens earlier on the key_type itself. At least that's the only logical explanation I could think of.
My suggestion would be to make the key_type const int* and keep your function parameter as is. After all, why would you need a pointer to a mutable value as a key to a map ?