Compiler seeming to change my const parameter [duplicate] - c++

This question already has answers here:
constant pointer vs pointer on a constant value [duplicate]
(11 answers)
C++: combine const with template arguments
(6 answers)
Closed 6 years ago.
I am trying to use my template class's Contains() method, but I am getting a strange argument conversion error.
error C2664: 'bool DynamicArray::Contains(const E) const'
: cannot convert argument 1 from 'const Joint *' to 'Joint *const '
with E=Joint
Conversion loses qualifiers
Here is the relevant template class code.
template <class E>
class DynamicArray
{
bool Contains (const E element) const;
// Other code...
};
template <class E>
bool DynamicArray<E>::Contains(const E element) const
{
// Other code...
}
The call made to the Contains method is done so here
bool ASMState::AnimatesJoint(const Joint* pJoint) const
{
return m_animatedJoints.Contains(pJoint);
}
Relevant template class code in ASMState.h
class ASMState
{
DynamicArray<Joint*> m_animatedJoints;
// Other members...
bool AnimatesJoint(const Joint* pJoint) const;
// Other methods...
};
If I remove the const in the AnimatesJoint function signature like so, bool ASMState::AnimatesJoint(Joint* pJoint) const then the code compiles. I would like to keep the const there if I can, but I do not know why that parameter seems to change from what I wrote. That is, from const Joint * to Joint *const according to the compiler.
I am using Visual Studio 2013 Express

Your class template DynamicArray deduces the type E as Joint *, and in the member function Contains you basically add a const to it. Now, as it was mentioned in the comments and already stated by the compiler error, this does not lead to const Joint* (which is the same as Joint const*), but rather to Joint * const -- adding a const does not correspond to a textual replacement where you'd simply replace E by your pointer type.
However, the problem here is not in adding the const to the pointer type taken by the function Contains, because this cast is allowed. The problem is rather that you pass a const pointer to the function Contains, but it is actually expecting a non-const pointer -- it's the cast from const Joint* to Joint * that is disallowed.
This is why removing the const in Contains(const E) will not solve this specific problem. I'd strongly suggest it nevertheless, because taking a parameter by const-value is almost never useful.
Rather, as you've already written, you need to remove the const in the function AnimatesJoint(const Joint* pJoint).

It seems that bool Contains (const E element) const expects an input of type const E, whereas in
return m_animatedJoints.Contains(pJoint);
you are passing an input of type const E *, in other words a pointer. Perhaps the solution is to change this line to:
return m_animatedJoints.Contains(*pJoint);
I guess that the error you are getting is because the compiler is trying to pass the input pJoint as const E where E = ... * const, i.e. a constant pointer.

Related

How to handle pointing types with templates, and const keywords?

I wanted to do a simple thing with templates and got surprised how not simple it is.
I have got :
An array which size is 9, let's say type const int *
I want it to pass it as an argument for an object construction, like so:
const int * myArray[9];
//filling array;
ObectType<int*> myObject(&myArray[0]);
With object constructor here :
template<class T>
class ObjectType
{
public:
ObjectType(const T * myArray) : myArrayMember(myArray) {};
const T * myArrayMember;
}
But I get the following error :
error: invalid conversion from ‘const int**’ to ‘int* const*’ [-fpermissive]
86 | ObectType<int*> myObject(&myArray[0]);
| ^~~~~~~~
| |
| const int**
Shouldn't const T * be replaced by const int** ?
I have tried to keep it simple, the real code is more complicated. Tell me if you need some information.
When you create myObject you are specifying the template parameter T with the argument int*. When substituted into the constructor argument T const *, this gives int * const *, which is incorrect.
You need T to be int const *, and you need to provide the correct template argument:
ObjectType<int const *> myObject(&myArray[0]);
Or from C++17, you can let it be deduced:
ObjectType myObject(&myArray[0]);
As mentioned in a comment by StoryTeller-UnslanderMonica, it's worth pointing out that the explanation in this answer seems obvious because I have chosen to use the "east const" notation which I find much more preferable for a number of reasons. You can read this Q&A for more details.
In this case, the benefit of this notation is that understanding how template argument substitution works becomes easier, and the correct substitution becomes more obvious.

Why use a reference to get a *char? Isn't better to not use references when we are using pointers?

(I will have a set of questions at the end)When i have to create a function that takes a pointer const char* as example, i never use a reference because i thought it makes no sense to use a reference to a pointer. But in a book (C++ Primer Fifht Edition) the author tell us that we have to create out version of a funcition called compare to compare our c-style string so we avoid using the general template version
template <>
int compare(const char* const &p1, const char* const &p2)
{ return strcmp(p1, p2); }
Yes he's actually declaring that the pointers are const. And, the author actually write another function that only takes string literals
template<size_t N, size_t M>
int compare(const char (&)[N], const char (&)[M]);
So my questions are:
Why is he using a reference to get a pointer? Copy a pointer or take a reference are equally expensive, isn't it?
Can't the first version take string literals?
Why is he using a template for a function that does not require template paremeters? He is trying to overload another template funcion put thats not enought reason, is it?
Why is he using a reference to get a pointer? Copy a pointer or take a reference are equally expensive, isn't it?
I am guessing that there is a function template:
template <typename T>
int compare(T const &t1, T const &t2) { ... }
and it is used elsewhere in the code base.
To make a specialization of the template for char const*, you'll have to use:
template <>
int compare(char const* const &p1, char const* const &p2) { ... }
// <-- T -->
Can't the first version take string literals?
Yes, it can.
Why is he using a template for a function that does not require template paremeters? He is trying to overload another template funcion put thats not enought reason, is it?
That's the syntax for specializing a function template. It can be a function overload instead of a function template only if the function is called without explicitly specifying the template parameter.
If you define
int compare(char const* const &p1, char const* const &p2) { ... }
it won't be called when the caller uses compare<char const*>(s1, s2).
It will be called when the callers uses compare(s1, s2).

How does C++ function template specialization work?

I am reading C++ Primer (5th Edition), 16.5, Defining a Function Template Specialization, and confused about the example given by author, let's see the following template function:
template <typename T> int compare(const T&, const T&);
and its specialization version:
template <>
int compare(const char* const &p1, const char* const &p2)
{
return strcmp(p1, p2);
}
Type of T will be const char *, but I don't think the function could be the specialization version of the template function, because I think const char* const &p1 can only be the specialization of T const & but const T &, I know I'm wrong, but I want to know why I am wrong.
Edit:
One thing to emphasize, if I invoke compare("hi", "mom"), it won't compile, that is to say, template <typename T> int compare(const T&, const T&) can not be initialized to int compare(const char* const &p1, const char* const &p2), I know T will be initialized with char[3] or char[4], but now that this won't compile, why won't compiler ignore this kind of initialization but choose one that will compile?
In C++, const T and T const mean exactly the same thing. Therefore const T & and T const & mean exactly the same thing.
It couldn't really be any other way, because references can never be changed to refer to something else (they are not "reseatable"). If you read T const & as "a reference which cannot be changed to refer to a different T", that's incorrect. It is "a reference to a T, which cannot be used to modify that T (and as with all references, cannot be changed to refer to a different T)."
const can be either before or after a type, except for a pointer type, where it must be on the right side.
Now the tricky part here is, that T in your example is set to const char*, which is a pointer type (pointer to a const char). The template says that T has to be const, and since it's a pointer type the const in the specialization has to be put after the type, hence you get const char* const.
It becomes a bit more clear when reading it aloud from right to left:
"a const pointer to a char that is const"
//Edit:
Why can't you call compare("hi", "mom");? Because those char arrays are treated as different types by your compiler (char[3] and char[4]), but the template specifies both parameters to be the same type.
This will match the template, however it will not match your specialization (as T is now char[2]):
compare("a", "b");
This works and uses your specialized method:
const char * hi = "hi";
const char * mom = "mom";
compare(hi, mom);
//Edit2:
"I know T will be initialized with char[3] or char[4], [...] why won't compiler ignore this kind of initialization but choose one that will compile?"
Because C++ is a strongly typed language. The compiler doesn't do guess work for you, it takes the types at face value. If they don't match, they don't match. It's your job as developer to do it right.

is there a way to pass this as const?

I have a class of items and a function that returns it's size.
I have the operator == which gets 2 const parameters of the class type and return the result of item1.size() == item2.size (). size function is non-parametres func and need only hidden this parameter.
The problem is when I try to use size on const reference of classes, it's give me an error:
'function' : cannot convert 'this' pointer from 'type1' to 'type2'
The compiler could not convert the this pointer from type1to type2.
This error can be caused by invoking a non-const member function on a const object. Possible resolutions:
Remove the const from the object declaration.
Add const to the member function.
The piece of code as it is on my problem:
bool operator==(const CardDeck& deck1, const CardDeck& deck2){
if (deck1.size() != deck2.size()) {
return false;
}
//...
}
The error:
'unsigned int CardDeck::size(void)' : cannot convert 'this' pointer from 'const CardDeck' to 'Cardeck&'
If I want that size will get the object as const, I must make it friend and pass the object as const refference or is there a way to tell size get the class type this as constant ???
Thanks for helping.
Most likely you forgot to qualify the size member function as const: size_t size() const { return /* compute/return size */; }
The alternative is that you really did typo CardDeck as Cardeck somewhere (the spelling from your error message).

Why is an array argument treated as a const array?

I am working with a member function that just sets the object's internal orientation to the values given in the argument:
void A::SetOrientation(float a[3]);
In another class, I have the following:
class B
{
public:
RestoreStateTo(A* const o_pA) const
private:
float d_orientation[3];
};
void
B::RestoreStateTo(A* const o_pA) const
{
o_pA->SetOrientation(d_orientation);
}
I get the following compiler error (with Visual Studio 2010):
error C2664: 'void A::SetOrientation(float [])' : cannot convert parameter 1 from 'const float [3]' to 'float []'
I found that I can avoid the issue with o_pA->SetOrientation(const_cast<float *>(d_orientation));, but I'd like to get a better grasp of what is going on.
I would appreciate an explanation as to why the array argument is converted to a const array as well as suggest the right approach to dealing with the error?
Because the prototype of your function void B::RestoreStateTo(A* const o_pA) const says you will not modify any member of B.
Since d_orientation is an attribute of B, it is const in this function.
Your SetOrientation function should take in a const,
void A::SetOrientation(const float a[3]);
Otherwise, it's possible that A::SetOrientation will modify the array you pass in. Because you have "B::RestoreStateTo(A* const o_pA) const", it means that the compiler won't let you pass a pointer to B::d_orientation as a non-const input, because A::SetOrientation has no guarantee not to modify it.
You have declared a const member function, and therefore all the members of this will be treated as const inside that function. So it shouldn't be surprising that d_orientation is treated as const.
It's the same principle that makes this code illegal:
const B* p = ...;
p->d_orientation[0] = 0.0f; // error, assigning member of `const` object