I have a function that takes some arguments of type T as such:
constexpr inline const bool isReflex(const T x1, const T y1, const T x2, const T y2, const T x, const T y)
Calling this function with items form a vector yiels to an error C2664: Cannot convert argument 1 from 'vector<T, std::allocator<_Ty>>' to 'const T':
vector<T>* v = new vector<T>; // I am not creating the vector myself, this is just for demonstration.
// The real vector is passed as const vector<T>* to a function executing the following:
if (isReflex(v[i-2], v[i-1], v[i], v[i+1], v[i+2], v[i+3]))
// ^^^^^^ error
This makes little sense to me, as I am not passing the vector but rather its contents. What could be causing this behaviour?
Edit
Ouch.
This is because v is not a vector, it is a pointer to vector. Therefore, you need a dereference operator:
if (isReflex((*v)[i-2], (*v)[i-1], (*v)[i], (*v)[i+1], (*v)[i+2], (*v)[i+3]))
The reason the error message may not look entirely clear is that [] operator applies to pointers as well, and behaves like a dereference operator with an offset. In other words, C++ compiler treats variable v as a built-in array of vectors, applies index [i-2] to that array, and reports an error, because the type of v[i-2] expression is a vector.
The real vector is passed as const vector<T>* to a function
You can make a reference variable to keep the old syntax:
const vector<T> *pv // function parameter
const vector<T>& v = *pv;
// This will work now
if (isReflex(v[i-2], v[i-1], v[i], v[i+1], v[i+2], v[i+3])) {
...
}
You're using the [n] operator on an type * object - in your case vector<T> *. Your compiler might be interpreting this as "give me the nth vector starting to count from this address" instead of "give me the nth element from the vector pointed to by this address".
Related
I am not sure the true meaning of const vector<int *> so I compiled the code below to get an idea but am now more confused.
vector<int *> v;
int x = 1, y = 2;
v.push_back(&x);
v.push_back(&y);
const vector<int *> w = v;
w[0] = &y; //failed. Element is a constant pointer?
*(w[0]) ++; //failed. Element pointer references to constant value?
If I had stopped here, I would have assumed that const vector<int *> is a vector of const int * const, but then I tried the following which clearly contradicted that assumption.
*(w[0]) += 3; //passed. Value not constant?
*(w[0]) = 20; //passed. Why...
Now *(w[0]) for reason unknown to me obviously treats ++ and += and assignment differently. I convinced myself that const vector only declares a constant object of the vector class and that the above results might depend on the actual implementation of the operator overloading of vector class. But I can't wrap my head around this. Can anyone help explain, please?
If it is relevant, I used g++ 4.2 on a Mac.
Why is dereferenced element in const vector of int pointers mutable?
For const vector<int *>, the element would be const pointer to non-const, i.e. int * const, so you can modify the object pointed by the pointer, but not the pointer itself.
According to Operator Precedence, postfix increment operator has higher precedence than operator*, so *(w[0]) ++; is equivalent to
* ((w[0]) ++);
The increment on the pointer is performed at first, then it fails. w[0] = &y; is also trying to modify the pointer, so it fails too.
On the other hand, (*w[0]) ++; (i.e. increment on the pointee) would be fine. And the following statements are fine too, because they're both modifying the objects pointed by the pointer, not the pointers.
*(w[0]) += 3; //passed.
*(w[0]) = 20; //passed.
It's a matter of operator precedence.
When you do *(w[0]) ++ you attempt to modify the pointer.
When you do *(w[0]) += 3 you modify the data pointed to by the pointer.
w is a const vector<int *>. The const qualifier is applied to the vector. Therefore, the corresponding const member function will be used for the operator[]:
const_reference operator[]( size_type pos ) const;
Since the vector is const-qualified and contains elements of type int * ( and not const int *), the type of the expression w[0] is int * const& (instead of const int *&). That is, it is a reference to a constant pointer to an int and not a reference to a pointer to a constant int: the constness is applied to the the pointer itself, not to the data being pointed.
By doing *(w[0]) += 3 you are not modifying the value of the pointer the vector returns (which is const), but the value this pointer is pointing to. Since this pointer is of type int * const (and not const int *), you can modify what it is pointing to, so it does work. However, doing w[0] = &y is performing an assignment on a constant pointer, so it does not compile.
const vector<T> lets you access its elements as T const & (i.e. const T &). In this case, T is int *, so this is int * const &, a const reference to a pointer that points to an int. The pointer is constant, but the int is not.
The type of the vector would have needed to be vector<int const *> (i.e. vector<const int*>) in which case the elements would be accessed via int const * const &.
Bottom line, constness is transitive with templates but not with pointers. And if you put pointers in templates, you get a bit of both behaviors.
Does const vector<A> mean that its elements are constas well?
In the code below,
v[0].set (1234); in void g ( const vector<A> & v )
produces the compiler error
const.cpp:28:3: error: member function 'set' not viable: 'this'
argument has
type 'const value_type' (aka 'const A'), but function is not marked const
Why?
But (*v[0]).set(1234); in void h ( const vector<A *> & v )
is OK for the compiler.
What's the difference between the versions?
// ...........................................................
class A {
private:
int a;
public:
A (int a_) : a (a_) { }
int get () const { return a; }
void set (int a_) { a = a_; }
};
// ...........................................................
void g ( const vector<A> & v ) {
cout << v[0].get();
v[0].set (1234);
} // ()
// ...........................................................
void h ( const vector<A *> & v ) {
cout << (*v[0]).get();
(*v[0]).set(1234);
} // ()
Yes, a const vector provides access to its elements as if they were const, that is, it only gives you const references. In your second function, it's not the objects of type A that are const, but pointers to them. A pointer being const does not mean that the object the pointer is pointing to is const. To declare a pointer-to-const, use the type A const *.
The first version
v[0].set (1234);
does not compile because it tries to change the vector's first element returned to it by reference. The compiler thinks it's a change because set(int) is not marked const.
The second version, on the other hand, only reads from the vector
(*v[0]).set(1234);
and calls set on the result of the dereference of a constant reference to a pointer that it gets back.
When you call v[0] on a const vector, you get back a const reference to A. When element type is a pointer, calling set on it is OK. You could change the second example to
v[0]->set(1234);
and get the same result as before. This is because you get a reference to a pointer that is constant, but the item pointed to by that pointer is not constant.
So a const object can only call const methods. That is:
class V {
public:
void foo() { ... } // Can't be called
void bar() const { ... } // Can be called
};
So let's look at a vector's operator[]:
reference operator[]( size_type pos );
const_reference operator[]( size_type pos ) const;
So when the vector object is const, it will return a const_reference.
About: (*v[0]).set(1234);
Let's break this down:
A * const & ptr = v[0];
A & val = *ptr;
val.set(1234);
Note that you have a constant pointer to variable data. So you can't change what is pointed at, but you can change the value that the pointer points at.
Yes, because std::vector is a value-type rather than a reference type.
To simplify things: An std::vector considers the values in its buffer as part of itself, so that changing them means changing the vector. This may be confusing if we only think of a vector as holding a pointer to an allocated buffer and the size: We don't change these two fields when we change elements in the buffer.
It's the opposite than for pointers, which are reference-types; if you change the pointed-to value you haven't changed the pointer itself.
The fact that std::vector is a value-type is a design choice - it's not something inherent in the C++ language. Thus, for example, the std::span class is also basically a pair of a pointer and a size, but an std::span can be const while you can still change the pointed-to elements. (There are other differences between spans and vectors.)
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 ?
int* test_prt(const vector<int>* array, const int index)
{
return array+index;
}
why the first argument can't be const ? under this circumstance, the "const" means what can not be modified?
It is common to place const after the type name, because it enables the "const to the left rule".
This is the same as what you wrote:
int* test_prt(vector<int> const* array, const int index)
{
return array+index;
}
Here, the item to the left is the vector, so this is a pointer to constant vector.
If you don't use this convention, the only special case is when const is all the way on the left (there is nothing to the left of const), which means it applies to the thing immediately to the right of the const.
array + index is not modifying either array or index, it simply computes the sum between the two operands without affecting the original, so the code is working as it's supposed to. The const here means that both the vector and index cannot change in any way. For example, if I attempt to change one of arrays elements, I get an error:
array[0] = 5; // error! assignment of read-only value
The same applies to index. By the way, I think your function signature should have been written as:
int* test_prt(const vector<int>& array, const int index)
{
return &array[index];
// or &array.at(index) which performs bounds checking
}
That is, we take the vector by reference and use an offset from the first element pointed to by its internal buffer. Taking by reference is essential as it avoids a copy and returning a pointer to a local copy would be Undefined Behavior. Your original code would have only worked if array was an actual array of vectors!
And just to be clear, here's how you would call the function:
std::vector<int> v = {1, 2, 3, 4, 5};
std::cout << test_ptr(v, 1); // prints the address of the 2nd element
I would also remove the "const" in front of the integer.
As it's an arg passed by value,
int* test_prt(vector<int> const* array, int index)
{
return array+index;
}
For the following definition of
const vector3F operator*(const vector3F &v, float s);
There are two const, what are their respective usages?
The const-reference in the argument means that you don't change v, so you can pass constant vectors (and temporaries!) to the function. That's a Good Thing.
The constant by-value return is sort of a gimmick. It prevents you from writing things like this:
vector3F v = get_vector();
vector3F w = v;
(v * 1.5) = w; // outch! Cannot assign to constant, though, so we're good.
Returning by-value as constant is problematic, though, since it interferes with C++11's rvalue references and move semantics:
move_me(v * 1.5); // cannot bind to `vector3F &&` :-(
Because of that, and because an abuse like the one I showed above is fairly unlikely to happen by accident, it's probably best to return by value only as non-constant.
The first const indicates that the return value is constant and can not be altered (which, by the way, is a bad idea for the multiplication operator):
const Vector3F v = myvector*100.0;
v.x = 0; // error: the vector is constant and can not be altered
The second const indicates that the argument "v" is constant:
const vector3F operator*(const vector3F &v, float s)
{
v.x = 0; // error: "v" is constant
}