A QList<T *> can't easily be const-correct. Consider the function
void f(QList<T *> list)
{
list[0]->constFunction();
}
I can change f to
void f(QList<const T *> list)
but then I can't do
f(QList<T *>()); //Compile error
anymore, since the compiler can't implicitely cast QList<T *> to QList<const T *>. However, I can explicitely reinterpret-cast the QList as follows:
template <typename T> inline QList<const T *> &constList(const QList<T *> &list)
{
return (QList<const T *> &)list;
}
This enables me to use the constList template function to cast any QList<T *> into a QList<const T *>, as in
f(constList(QList<T *>()));
and it seems to work fine, but is it actually safe to do this?
The casting function you're considering, …
template< class T >
inline QList<T const*>& constList( QList<T*> const& list )
{
return reinterpret_cast< QList<T const*>& >(
const_cast< QList<T*>& >( list )
);
}
… may be practical (probably QList does not change its object representation depending on the const-ness of the element type), but it can break const correctness.
First, because casting away the const-ness of the list itself is not const correct: it allows you to change an originally const list.
But even if that formal argument const is removed, like …
template< class T >
inline QList<T const*>& constList( QList<T*>& list )
{
return reinterpret_cast< QList<T const*>& >(
list
);
}
… there is still a const correctness problem.
The reason is that the list constitutes an additional level of indirection, and with your function is not itself const. Thus, after using your function to get a reference to the list with alleged pointer-to-const elements, you can store in that list a pointer to something that is really const. And then you can use the original list to modify that really const item, bang.
It's the same reason that there is no implicit conversion from T** to T const**.
What you can do without such problems is, with an already const list of pointers to objects, make those pointed to objects const:
template< class T >
inline QList<T const*> const& constList( QList<T*> const& list )
{
return reinterpret_cast< QList<T const*> const& >(
list
);
}
Formally there's still the reinterpret_cast as a potential problem, but anyone specializating the representation of QList on constness of elements would presumably deserve whatever they got. :-)
Cheers & hth.,
Const correctness idea is weak and what you found is one of the many reasons. Constness concept only captures a single bit of semantic (change/don't change) and does so badly and at a quite high syntax cost that sometimes even requires code duplication.
One problem of this concept is that it doesn't scale well by composition: for example I could be interested to pass a function a const vector of non-const objects, or a non-const vector of const objects, or a const vector of const objects and getting the syntax right is expensive... just consider how standard c++ was forced to introduce const_iterator because that is of course different from a const iterator.
Over the years I moved from the zealot position of using const correctness in every place I could to the opposite of using it only where I'm forced to. Experience taught me that code that is not obsessed with const correctness becomes cleaner and that const correctness machinery never really catches (at least for me) any logical error but only errors about the const correctness machinery itself. Unfortunately const correctness is one of the things in C++ that you are forced to use because C++ rules say so and there is no way to just avoid using it.
I know I'm going to be downvoted for this post but my suggestion is to keep a critical eye on whatever you read about C++ on this subject; keep the brain on and judge objectively. Just because would be nice for something being true (e.g. that const correctness helps the programmer) unfortunately it doesn't mean it's really true.
No matter how big is the name that signed the book.
Also remember that if a position requires all the rest of the world to be wrong then it's probably a good idea to be at least a little skeptic: most languages, even those born after C++, don't implement this concept.
Related
By this question I am also trying to understand fundamentals of C++, as I am very new to C++. There are many good answers to problem of sorting a vector/list of custom classes, like this. In all of the examples the signature of comparator functions passed to sort are like this:
(const ClassType& obj1, const ClassType& obj2)
Is this signature mandatory for comparator functions? Or we can give some thing like this also:
(ClassType obj1, ClassType obj2)
Assuming I will modify the body of comparator accordingly.
If the first signature is mandatory, then why?
I want to understand reasons behind using const and reference'&'.
What I can think is const is because you don't want the comparator function to be able to modify the element. And reference is so that no multiple copies are created.
How should my signature be if I want to sort a vector which contains pointers to objects of custom class? Like (1) or (2) (see below) or both will work?
vertor to be sorted is of type vector
(1)
(const ClassType*& ptr1, const ClassType*& ptr2)
(2)
(ClassType* ptr1, ClassType* ptr2)
I recommend looking through This Documentation.
It explains that the signature of the compare function must be equivalent to:
bool cmp(const Type1& a, const Type2& b);
Being more precise it then goes on to explain that each parameter needs to be a type that is implicitly convertable from an object that is obtained by dereferencing an iterator to the sort function.
So if your iterator is std::vector<ClassType*>::iterator then your arguments need to be implicitly convertable to ClassType*.
If you are using something relatively small like an int or a pointer then I would accept them by value:
bool cmp(const ClassType* ptr1, const ClassType* ptr2) // this is more efficient
NOTE: I made them pointers to const because a sort function should not modify the values it is sorting.
(ClassType obj1, ClassType obj2)
In most situations this signature will also work, for comparators. The reason it is not used is because you have to realize that this is passing the objects by value, which requires the objects to be copied.
This will be a complete waste. The comparator function does not need to have its own copies of its parameters. All it needs are references to two objects it needs to compare, that's it. Additionally, a comparator function does not need to modify the objects it is comparing. It should not do that. Hence, explicitly using a const reference forces the compiler to issue a compilation error, if the comparator function is coded, in error, to modify the object.
And one situation where this will definitely not work is for classes that have deleted copy constructors. Instances of those classes cannot be copied, at all. You can still emplace them into the containers, but they cannot be copied. But they still can be compared.
const is so you know not to change the values while you're comparing them. Reference is because you don't want to make a copy of the value while you're trying to compare them -- they may not even be copyable.
It should look like your first example -- it's always a reference to the const type of the elements of the vector.
If you have vector, it's always:
T const & left, T const & right
So, if T is a pointer, then the signature for the comparison includes the comparison.
There's nothing really special about the STL. I use it for two main reasons, as a slightly more convenient array (std::vector) and because a balanced binary search tree is a hassle to implement. STL has a standard signature for comparators, so all the algorithms are written to operate on the '<' operation (so they test for equality with if(!( a < b || b < a)) ). They could just as easily have chosen the '>' operation or the C qsort() convention, and you can write your own templated sort routines to do that if you want. However it's easier to use C++ if everything uses the same conventions.
The comparators take const references because a comparator shouldn't modify what it is comparing, and because references are more efficient for objects than passing by value. If you just want to sort integers (rarely you need to sort just raw integers in a real program, though it's often done as an exercise) you can quite possibly write your own sort that passes by value and is a tiny bit faster than the STL sort as a consequence.
You can define the comparator with the following signature:
bool com(ClassType* const & lhs, ClassType* const & rhs);
Note the difference from your first option. (What is needed is a const reference to a ClassType* instead of a reference to a const ClassType*)
The second option should also be good.
I have a class like
struct S {
bool foo(const AType& v) const {
return values.count(&v); // compile error due to the constness of v
}
private:
std::set<AType*> values;
};
This is a simplified version. In real code, foo does some complex things.
The code produces an error
invalid conversion from ‘const AType*’ to ‘std::set<AType*>::key_type {aka AType*}’
I think foo should take 'const AType& v' because it does not mutate v.
The type of the member variable 'values' can not be std::set<const AType*> because some methods of the struct S call non-const methods of the elements contained in 'values'.
I can cast constness of 'v' away:
bool foo(const AType& v) const {
return values.count((AType*) &v);
}
But I think this may not be a good solution in general.
What solution could I have?
template<class T>
struct const_ptr_compare:std::less<T const*> {
typedef void is_transparent;
};
std::set<AType*, const_ptr_compare<AType>> values;
and bob is your uncle.
I defined a comparator that can transparently handle T const* comparisons as well as T* comparisons. I then told std::set that it is transparent.
The is_transparent technique is a C++14 enhancement to std::set. Your compiler may already have support for it.
If you want to be able to pass in base-classes-of-T a bit more work needs to be done. You need a function object that tests its two arguments (each pointers to const) to see which is a base class of the other, and does a std::less<base const*>{}(lhs, rhs) using that base class. However, that is going further down the rabbit hole than we need to.
A C++11/03 approach could involve making the editable portions of AType mutable, and having a const AType* set. Failing that, a const_cast<AType*> is reasonable.
Please don't use a C-style cast: they are almost never required, and they can be accidentally too powerful.
The root of the problem is this:
The type of the member variable 'values' can not be std::set because some methods of the struct S call non-const methods of
the elements contained in 'values'.
You are not supposed to mutate the set key in any way.
You should be using a map, where the actual key parts are in the key and are const, and the mutating parts are in the value and are non-const.
If this is impractical for your design, I understand. The const_cast workaround will solve the issue. It feels inelegant, but that's because your usage of set is inelegant.
This is just one of the many problems of the const correctness concept: it doesn't scale by composition.
The casting solution is just ok, if you feel it's "dirty" in some way probably you're overestimating cleanness of the const concept.
Consider that defining special types for const_iterator and const_reverse_iterator was needed or that sometimes const correctness requires actual code duplication (e.g. in many cases for operator[]) or that if you've an object containing a non-const pointer you can mutate the pointed-to object freely and even delete it in const members.
Your cast doesn't look that bad in comparison :-)
As title says, is it allowed? If not, are they sharing the same interface or abstract class anyway? I did not find any reference from online documents. but looks unordered_map and map are using the same functions.
No, they're totally different and even if you force it with reinterpret_cast, it'll just go horribly wrong at runtime (ie, the dreaded Undefined Behaviour).
They're both STL containers, so deliberately have consistent interfaces.
That doesn't mean they're the same thing internally.
They're two unrelated types. If you want to construct one based on the other, you need to use constructors that take iterator range (C++11):
template <class InputIterator>
map(InputIterator first, InputIterator last,
const Compare& comp = Compare(), const Allocator& = Allocator());
template <class InputIterator>
unordered_map(InputIterator f, InputIterator l,
size_type n = see below,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
STL classes do not use virtual functions; there is no consistent base class you can cast through (ie, there's no equivalent to Java's java.util.Map). You can't cast between std::unordered_map and std::map any more than you could cast between HashMap and TreeMap in java - they're completely different types and cannot be used equivalently.
If you want a function to be able to take multiple types of STL containers, just use a template function:
template<typename Map>
void somefunc(Map &mymap) {
// ...
}
You can reinterpret cast one thing to any other thing, even if it makes no sense. The compiler won't stop you if you force it, which is essentially what reinterpret cast is. Casting map to unordered_map makes no sense & won't work.
Why do you want to cast map to unordered_map? They have the same interface. You gain nothing by casting it.
So, I realize that const T& and T const& are identical and both mean a reference to a const T. In both cases, the reference is also constant (references cannot be reassigned, unlike pointers). I've observed, in my somewhat limited experience, that most C++ programmers use const T&, but I have come across a few people who use T const&. I use const T& simply because I learned it that way, and so T const& looks a little bit funny to me. What is the reason that you use the variant that you use? Do any of you work at an organization for which the coding standards mandate the use of one variant over the other?
Edit
Based on the answers, it would appear that one reason for choosing between the two is whether you want to read it like the compiler (right-to-left) or like English (left-to-right). If one reads it like the compiler, then "T const&" reads as "& (reference) const (to a constant) T (of type T)". If one reads it like English, from left-to-right, then "const T&" is read as "a constant object of type T in the form of a reference". I prefer to read it like English prose, but I can certainly see the sense in interpreting it the way that the compiler does.
No one has answered the organization or coding standards question, but I strongly suspect that most organizations do not mandate one over the other, although they might strive for consistency.
I think some people simply prefer to read the declarations from right to left. const applies to the left-hand token, except when there is nothing there and it applies on the right-hand token. Hence const T& involves the "except"-clause and can perhaps be thought more complicated (in reality both should be as easy to understand).
Compare:
const T* p; (pointer to T that is const)
T const* p; (pointer to const T) //<- arguable more natural to read
T* const p; (const pointer to T)
This will make a difference when you have more then one const/volatile modifiers. Then putting it to the left of the type is still valid but will break the consistency of the whole declaratiion. For example:
T const * const *p;
means that p is a pointer to const pointer to const T and you consistenly read from right to left.
const T * const *p;
means the same but the consistency is lost and you have to remember that leftmost const/volatile is bound to T alone and not T *.
If you find this discussion interesting, you'd probably find this article by Dan Saks interesting. It doesn't directly address your question, but explains why he prefers
VP const foo[];
to
const VP foo[];
It's because given
typedef void *VP;
you could easily be misled into thinking that the second example above means
const void *foo[]; // Wrong, actually: void *const foo[];
but the first example is harder to misinterpret.
My reasoning is as follows:
It does seem to roll off the tongue better if you write "const T&" but when you do that you end up with the ambiguous, "constant T reference." I've seen this cause problems more than once in the understandability of code that allowed someone, even semi-experienced, to misinterpret what something meant or how to declare a more complex type.
I can't think of any example right now but more than once I've answered questions about type declarations and constness where the problem was caused by the habit of using "const T &" instead of "T const &". I used to write it that way as well and when I became a Sr. Developer, someone in charge of mentoring and creating code standards in projects, I found it much easier for entry level developers when I force everyone to use "T const&". I suppose one rather trivial, rookie mistake would be why does this code compile?
const T* t = f();
t = 0; // assignment to const?? - no, it is not the T* that is const, just the T.
When you learn to read it the way that the compiler does it becomes much easier to understand what any given complex type is as well as allowing you to more readily declare complex types when you need to. By complex types I'm talking about things such as:
T const * const &
When you know that the compiler reads right to left, inside to out, what that means becomes quite apparent and when it is necessary to write one you can do so easily: reference to constant pointer to a constant T. Now write the declaration of a "reference to a pointer to a constant pointer to a T". If you simply use left to right notation I think you'll find this quite easy.
In short, though it initially seems unnatural teaching oneself to use right->left grammar ALL the time, instead of only when it is required (because it often is), you'll find it much easier to remember what a line of code means and how to write what you mean. It's sort of along the same lines of why I disallow "," in declarations:
T* ptr1 = 0, ptr2 = 0; // oops!!!
// do it this way please!
T* ptr1 = 0;
T* ptr2 = 0;
Technically it's all the same but when you try to get a bunch of people of varying capacities working on the same thing you'll tend to make sure everyone uses whatever method is the easiest to understand and use. My experience has taught me that "T const&" is that method.
I think is personal preference. There is no difference between the two variants.
Being as code is predominantly English-based, programmers tend to read left to right, so const T& reads naturally to us where the compiler reads it inside out right to left so T const& reads naturally(reference to a const T)
That's because some find it helpful to read the declaration right-to-left.
char const*
const char*
are both pointer to const char.
I used to be a strong advocate of const T& because it does read logically left-to-right (it's a constant T reference). (And probably there's some bias since most code I'd encountered to that point was written that way.)
Over the years I've encountered some corner cases (such as multiple pointer/reference layers, multiple variable declarations, and method pointers) which strain things a little for the reasons other people have already answered. Often introducing additional typedefs help you "unstick" these cases to some extent, but then you have to come up with a name for something even if it's only used once.
The tipping point for me was the introduction of auto in C++11, especially when combined with range-based-for. With that, I've flipped and now strongly prefer T const& (or "reference to constant T", reading right to left) instead. Not only is it more consistent with how the compiler actually parses, it means that when you replace the type with auto, this always ends up at the left, which I think reads better.
Compare:
for (const T& a : list) for (T& a : list)
for (T const& a : list) for (T& a : list)
for (const auto& a : list) for (auto& a : list)
for (auto const& a : list) for (auto& a : list)
Note also the column on the right, where I've added the non-const version of the same. At least for me, the const vs. non-const and auto vs. explicit all seem most consistent in the cases where const appears after T.
But this is a style choice, and as such there is no absolutely correct answer.
If the const and & get far apart, as in
krog::FlamBlott<std::map<HurkleKey,Spleen<int>>
speckleFlams( const krog::Floonage & floon, const std::map<krog::HackleSpoon,std::vector<krog::HeckleObservation<OBSN>>> & obsmap);
krog::FlamFinagleReport
finagleFlams( const krog::Floonage & floon, std::map<krog::HackleSpoon,std::vector<krog::HeckleObservation<OBSN>>> & obsmap)
... then it becomes easy to miss that the first 'obsmap' is a const reference and the second is not.
I use the T const& notation myself, and I recommend it to everyone. The reason is the ergonomics - i find it to reduce eye movement.
#grego already made a great point on how "const" being close to "&" prevents bugs. Usualy when I find "&" I want to know if it is const or not, and moving my eyes left-and-right is not what I want to do all the time.
There is another reason for <T const&> not mentioned before - code alignment. Let's see which declaration is easier to read:
void append(std::string& str,
const std::vector<std::string>& elements,
const char* delimiter);
void append(std::string& str,
std::vector<std::string> const& elements,
char const* delimiter);
<const T&> hides the type in the middle, while <T const&> exposes it on the left side. <T const&> aligns the type to the same column sliding from top to bottom can be with much less left-right eye movement when looking for a specific argument's type.
For the same reason I like the trailing return type notation (auto f(...) -> R)
I generally prefer constness, but recently came across a conundrum with const iterators that shakes my const attitude annoys me about them:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
The idea that I'm trying to express here, of course, is that the passed in list cannot/willnot be changed, but once I make the list reference const I then have to use 'const_iterator's which then prevent me from doing anything with modifing the result (which makes sense).
Is the solution, then, to give up on making the passed in container reference const, or am I missing another possibility?
This has always been my secret reservation about const: That even if you use it correctly, it can create issues that it shouldn't where there is no good/clean solution, though I recognize that this is more specifically an issue between const and the iterator concept.
Edit: I am very aware of why you cannot and should not return a non-const iterator for a const container. My issue is that while I want a compile-time check for my container which is passed in by reference, I still want to find a way to pass back the position of something, and use it to modify the non-const version of the list. As mentioned in one of the answers it's possible to extract this concept of position via "advance", but messy/inefficient.
If I understand what you're saying correctly, you're trying to use const to indicate to the caller that your function will not modify the collection, but you want the caller (who may have a non-const reference to the collection) to be able to modify the collection using the iterator you return. If so, I don't think there's a clean solution for that, unless the container provides a mechanism for turning a const interator into a non-const one (I'm unaware of a container that does this). Your best bet is probably to have your function take a non-const reference. You may also be able to have 2 overloads of your function, one const and one non-const, so that in the case of a caller who has only a const reference, they will still be able to use your function.
It's not a trap; it's a feature. (:-)
In general, you can't return a non-const "handle" to your data from a const method. For example, the following code is illegal.
class Foo
{
public:
int& getX() const {return x;}
private:
int x;
};
If it were legal, then you could do something like this....
int main()
{
const Foo f;
f.getX() = 3; // changed value of const object
}
The designers of STL followed this convention with const-iterators.
In your case, what the const would buy you is the ability to call it on const collections. In which case, you wouldn't want the iterator returned to be modifiable. But you do want to allow it to be modifiable if the collection is non-const. So, you may want two interfaces:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
MyList::iterator find( MyList & list, int identifier )
{
// do some stuff to find identifier
return retItor;
}
Or, you can do it all with one template function
template<typename T>
T find(T start, T end, int identifier);
Then it will return a non-const iterator if the input iterators are non-const, and a const_iterator if they are const.
What I've done with wrapping standard algorithms, is have a metaobject for determining the type of container:
namespace detail
{
template <class Range>
struct Iterator
{
typedef typename Range::iterator type;
};
template <class Range>
struct Iterator<const Range>
{
typedef typename Range::const_iterator type;
};
}
This allows to provide a single implementation, e.g of find:
template <class Range, class Type>
typename detail::Iterator<Range>::type find(Range& range, const Type& value)
{
return std::find(range.begin(), range.end(), value);
}
However, this doesn't allow calling this with temporaries (I suppose I can live with it).
In any case, to return a modifiable reference to the container, apparently you can't make any guarantees what your function does or doesn't do with the container. So this noble principle indeed breaks down: don't get dogmatic about it.
I suppose const correctness is more of a service for the caller of your functions, rather that some baby-sitting measure that is supposed to make sure you get your simple find function right.
Another question is: how would you feel if I defined a following predicate and then abused the standard find_if algorithm to increment all the values up to the first value >= 3:
bool inc_if_less_than_3(int& a)
{
return a++ < 3;
}
(GCC doesn't stop me, but I couldn't tell if there's some undefined behaviour involved pedantically speaking.)
1) The container belongs to the user. Since allowing modification through the predicate in no way harms the algorithm, it should be up to the caller to decide how they use it.
2) This is hideous!!! Better implement find_if like this, to avoid this nightmare (best thing to do, since, apparently, you can't choose whether the iterator is const or not):
template <class Iter, class Pred>
Iter my_find_if(Iter first, Iter last, Pred fun)
{
while (first != last
&& !fun( const_cast<const typename std::iterator_traits<Iter>::value_type &>(*first)))
++first;
return first;
}
Although I think your design is a little confusing (as others have pointed iterators allow changes in the container, so I don't see your function really as const), there's a way to get an iterator out of a const_iterator. The efficiency depends on the kind of iterators.
#include <list>
int main()
{
typedef std::list<int> IntList;
typedef IntList::iterator Iter;
typedef IntList::const_iterator ConstIter;
IntList theList;
ConstIter cit = function_returning_const_iter(theList);
//Make non-const iter point to the same as the const iter.
Iter it(theList.begin());
std::advance(it, std::distance<ConstIter>(it, cit));
return 0;
}
Rather than trying to guarantee that the list won't be changed using the const keyword, it is better in this case to guarantee it using a postcondition. In other words, tell the user via comments that the list won't be changed.
Even better would be using a template that could be instantiated for iterators or const_iterators:
template <typename II> // II models InputIterator
II find(II first, int identifier) {
// do stuff
return iterator;
}
Of course, if you're going to go to that much trouble, you might as well expose the iterators of MyList to the user and use std::find.
If you're changing the data directed by the iterator, you're changing the list.
The idea that I'm trying to express here, of course, is that the passed in list cannot/willnot be changed, but once I make the list reference const I then have to use 'cons_iterator's which then prevent me from doing anything with the result.
What is "dong anything"? Modifying the data? That's changing the list, which is contradictory to your original intentions. If a list is const, it (and "it" includes its data) is constant.
If your function were to return a non-const iterator, it would create a way of modifying the list, hence the list wouldn't be const.
You are thinking about your design in the wrong way. Don't use const arguments to indicate what the function does - use them to describe the argument. In this case, it doesn't matter that find() doesn't change the list. What matters is that find() is intended to be used with modifiable lists.
If you want your find() function to return a non-const iterator, then it enables the caller to modify the container. It would be wrong for it to accept a const container, because that would provide the caller with a hidden way of removing the const-ness of the container.
Consider:
// Your function
MyList::iterator find(const MyList& list, int identifier);
// Caller has a const list.
const MyList list = ...
// but your function lets them modify it.
*( find(list,3) ) = 5;
So, your function should take a non-const argument:
MyList::iterator find(MyList& list, int identifier);
Now, when the caller tries to use your function to modify her const list, she'll get a compilation error. That's a much better outcome.
If you're going to return a non-const accessor to the container, make the function non-const as well. You're admitting the possibility of the container being changed by a side effect.
This is a good reason the standard algorithms take iterators rather than containers, so they can avoid this problem.