Why do some people prefer "T const&" over "const T&"? - c++

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)

Related

Why is C++ auto risky [duplicate]

It seems that auto was a fairly significant feature to be added in C++11 that seems to follow a lot of the newer languages. As with a language like Python, I have not seen any explicit variable declaration (I am not sure if it is possible using Python standards).
Is there a drawback to using auto to declare variables instead of explicitly declaring them?
The question is about drawbacks of auto, so this answer highlights some of those. A drawback of using a programming language feature (in this case, a facility associated with a language keyword) does not mean that feature is unacceptable, nor does it mean that feature should be avoided entirely. It means there are disadvantages along with advantages, so a decision to use auto type deduction over alternatives must consider engineering trade-offs.
When used well, auto has several advantages as well - which is not the subject of the question. The drawbacks result from ease of abuse, and from increased potential for code to behave in unintended or unexpected ways.
The main drawback is that, by using auto, you don't necessarily know the type of object being created. There are also occasions where the programmer might expect the compiler to deduce one type, but the compiler adamantly deduces another.
Given a declaration like
auto result = CallSomeFunction(x,y,z);
you don't necessarily have knowledge of what type result is. It might be an int. It might be a pointer. It might be something else. All of those support different operations. You can also dramatically change the code by a minor change like
auto result = CallSomeFunction(a,y,z);
because, depending on what overloads exist for CallSomeFunction() the type of result might be completely different - and subsequent code may therefore behave completely differently than intended. You might suddenly trigger error messages in later code(e.g. subsequently trying to dereference an int, trying to change something which is now const). The more sinister change is where your change sails past the compiler, but subsequent code behaves in different and unknown - possibly buggy - ways. For example (as noted by sashoalm in comments) if the deduced type of a variable changes an integral type to a floating point type - and subsequent code is unexpectedly and silently affected by loss of precision.
Not having explicit knowledge of the type of some variables therefore makes it harder to rigorously justify a claim that the code works as intended. This means more effort to justify claims of "fit for purpose" in high-criticality (e.g. safety-critical or mission-critical) domains.
The other, more common drawback, is the temptation for a programmer to use auto as a blunt instrument to force code to compile, rather than thinking about what the code is doing, and working to get it right.
This isn't a drawback of auto in a principled way exactly, but in practical terms it seems to be an issue for some. Basically, some people either: a) treat auto as a savior for types and shut their brain off when using it, or b) forget that auto always deduces to value types. This causes people to do things like this:
auto x = my_obj.method_that_returns_reference();
Oops, we just deep copied some object. It's often either a bug or a performance fail. Then, you can swing the other way too:
const auto& stuff = *func_that_returns_unique_ptr();
Now you get a dangling reference. These problems aren't caused by auto at all, so I don't consider them legitimate arguments against it. But it does seem like auto makes these issue more common (from my personal experience), for the reasons I listed at the beginning.
I think given time people will adjust, and understand the division of labor: auto deduces the underlying type, but you still want to think about reference-ness and const-ness. But it's taking a bit of time.
Other answers are mentioning drawbacks like "you don't really know what the type of a variable is." I'd say that this is largely related to sloppy naming convention in code. If your interfaces are clearly-named, you shouldn't need to care what the exact type is. Sure, auto result = callSomeFunction(a, b); doesn't tell you much. But auto valid = isValid(xmlFile, schema); tells you enough to use valid without having to care what its exact type is. After all, with just if (callSomeFunction(a, b)), you wouldn't know the type either. The same with any other subexpression temporary objects. So I don't consider this a real drawback of auto.
I'd say its primary drawback is that sometimes, the exact return type is not what you want to work with. In effect, sometimes the actual return type differs from the "logical" return type as an implementation/optimisation detail. Expression templates are a prime example. Let's say we have this:
SomeType operator* (const Matrix &lhs, const Vector &rhs);
Logically, we would expect SomeType to be Vector, and we definitely want to treat it as such in our code. However, it is possible that for optimisation purposes, the algebra library we're using implements expression templates, and the actual return type is this:
MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);
Now, the problem is that MultExpression<Matrix, Vector> will in all likelihood store a const Matrix& and const Vector& internally; it expects that it will convert to a Vector before the end of its full-expression. If we have this code, all is well:
extern Matrix a, b, c;
extern Vector v;
void compute()
{
Vector res = a * (b * (c * v));
// do something with res
}
However, if we had used auto here, we could get in trouble:
void compute()
{
auto res = a * (b * (c * v));
// Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}
It makes your code a little harder, or tedious, to read.
Imagine something like that:
auto output = doSomethingWithData(variables);
Now, to figure out the type of output, you'd have to track down signature of doSomethingWithData function.
One of the drawbacks is that sometimes you can't declare const_iterator with auto. You will get ordinary (non const) iterator in this example of code taken from this question:
map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");
Like this developer, I hate auto. Or rather, I hate how people misuse auto.
I'm of the (strong) opinion that auto is for helping you write generic code, not for reducing typing.
C++ is a language whose goal is to let you write robust code, not to minimize development time.
This is fairly obvious from many features of C++, but unfortunately a few of the newer ones like auto that reduce typing mislead people into thinking they should start being lazy with typing.
In pre-auto days, people used typedefs, which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be, which removes one of the most powerful C++ tools from the toolbox and risks breaking their code.
Generally, if you use auto, it should be because your code works for any reasonable type, not because you're just too lazy to write down the type that it should work with.
If you use auto as a tool to help laziness, then what happens is that you eventually start introducing subtle bugs in your program, usually caused by implicit conversions that did not happen because you used auto.
Unfortunately, these bugs are difficult to illustrate in a short example here because their brevity makes them less convincing than the actual examples that come up in a user project -- however, they occur easily in template-heavy code that expect certain implicit conversions to take place.
If you want an example, there is one here. A little note, though: before being tempted to jump and criticize the code: keep in mind that many well-known and mature libraries have been developed around such implicit conversions, and they are there because they solve problems that can be difficult if not impossible to solve otherwise. Try to figure out a better solution before criticizing them.
auto does not have drawbacks per se, and I advocate to (hand-wavily) use it everywhere in new code. It allows your code to consistently type-check, and consistently avoid silent slicing. (If B derives from A and a function returning A suddenly returns B, then auto behaves as expected to store its return value)
Although, pre-C++11 legacy code may rely on implicit conversions induced by the use of explicitly-typed variables. Changing an explicitly-typed variable to auto might change code behaviour, so you'd better be cautious.
Keyword auto simply deduce the type from the return value. Therefore, it is not equivalent with a Python object, e.g.
# Python
a
a = 10 # OK
a = "10" # OK
a = ClassA() # OK
// C++
auto a; // Unable to deduce variable a
auto a = 10; // OK
a = "10"; // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0; // OK, implicit casting warning
Since auto is deduced during compilation, it won't have any drawback at runtime whatsoever.
What no one mentioned here so far, but for itself is worth an answer if you asked me.
Since (even if everyone should be aware that C != C++) code written in C can easily be designed to provide a base for C++ code and therefore be designed without too much effort to be C++ compatible, this could be a requirement for design.
I know about some rules where some well defined constructs from C are invalid for C++ and vice versa. But this would simply result in broken executables and the known UB-clause applies which most times is noticed by strange loopings resulting in crashes or whatever (or even may stay undetected, but that doesn't matter here).
But auto is the first time1 this changes!
Imagine you used auto as storage-class specifier before and transfer the code. It would not even necessarily (depending on the way it was used) "break"; it actually could silently change the behaviour of the program.
That's something one should keep in mind.
1At least the first time I'm aware of.
As I described in this answer auto can sometimes result in funky situations you didn't intend.
You have to explictly say auto& to have a reference type while doing just auto can create a pointer type. This can result in confusion by omitting the specifier all together, resulting in a copy of the reference instead of an actual reference.
One reason that I can think of is that you lose the opportunity to coerce the class that is returned. If your function or method returned a long 64 bit, and you only wanted a 32 unsigned int, then you lose the opportunity to control that.
I think auto is good when used in a localized context, where the reader easily & obviously can deduct its type, or well documented with a comment of its type or a name that infer the actual type. Those who don't understand how it works might take it in the wrong ways, like using it instead of template or similar. Here are some good and bad use cases in my opinion.
void test (const int & a)
{
// b is not const
// b is not a reference
auto b = a;
// b type is decided by the compiler based on value of a
// a is int
}
Good Uses
Iterators
std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();
..
std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();
// VS
auto vi = v.begin();
Function Pointers
int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
..
}
..
int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;
// VS
auto *f = test;
Bad Uses
Data Flow
auto input = "";
..
auto output = test(input);
Function Signature
auto test (auto a, auto b, auto c)
{
..
}
Trivial Cases
for(auto i = 0; i < 100; i++)
{
..
}
Another irritating example:
for (auto i = 0; i < s.size(); ++i)
generates a warning (comparison between signed and unsigned integer expressions [-Wsign-compare]), because i is a signed int. To avoid this you need to write e.g.
for (auto i = 0U; i < s.size(); ++i)
or perhaps better:
for (auto i = 0ULL; i < s.size(); ++i)
I'm surprised nobody has mentioned this, but suppose you are calculating the factorial of something:
#include <iostream>
using namespace std;
int main() {
auto n = 40;
auto factorial = 1;
for(int i = 1; i <=n; ++i)
{
factorial *= i;
}
cout << "Factorial of " << n << " = " << factorial <<endl;
cout << "Size of factorial: " << sizeof(factorial) << endl;
return 0;
}
This code will output this:
Factorial of 40 = 0
Size of factorial: 4
That was definetly not the expected result. That happened because auto deduced the type of the variable factorial as int because it was assigned to 1.

Isn't this const auto& redundant?

I found this code in one of Stroustrup's books:
void print_book(const vector<Entry>& book)
{
for (const auto& x : book) // for "auto" see §1.5
cout << x << '\n';
}
But the const seems redundant because x will be deduced to be a const_iterator since book is const in the parameter. Is const auto really better?
In my opinion, it looks explicit which is why it is better. So if I mean const, then I'd prefer to write it explicitly. It enhances local reasoning and thus helps others to understand the code comparatively easily — other programmers dont have to look at the declaration of book and infer that x is const as well.
Code isn't just for the compiler to read — it's also for humans to read. Conciseness is only good when it does not come at the expense of readability.
In the given code, deducing constness is immediate. With your proposed change, deducing constness would require the reader to accurately remember or review the definition of book.
If the reader is intended to be aware of constness, brevity would be counterproductive here.
It's much clearer seeing the keyword const. You immediately recognize that it's not going to be modified, if the const keyword wasn't there we wouldn't know unless we looked up to the function signature.
Using the const keyword explicitly is much better for the readers of the code.
Moreover, when you read the code below you expect that x is modifiable
for (auto& x : book) { ... }
And then when you try to modify x you'll find that it results in an error because book is const.
This is not clear code in my opinion. It's better to state in code that x is not going to be modified so that others that read the code will have their expectations right.
I'm going to take a different angle here. const and const_iterator mean completely different things.
const applied to an iterator means you cannot modify the iterator itself, but you can modify the element it points to, much like a pointer declared like int* const. const_iterator means you cannot modify the element it points to, but you can still modify the iterator itself (ie. you can increment and decrement it) much like a pointer declared like const int*.
std::vector<int> container = {1, 2, 3, 4, 5};
const std::vector<int> const_container = {6, 7, 8, 9, 0};
auto it1 = container.begin();
const auto it2 = container.begin();
auto it3 = const_container.begin();
const auto it4 = const_container.begin();
*it1 = 10; //legal
*it2 = 11; //legal
*it3 = 12; //illegal, won't compile
*it4 = 13; //illegal, won't compile
it1++; //legal
it2++; //illegal, won't compile
it3++; //legal
it4++; //illegal, won't compile
As you can see, the ability to modify the element the iterator points to depends only on whether it's an iterator or const_iterator, and the ability to modify the iterator itself depends only on whether the variable declaration for the iterator has a const qualifier.
Edit: I just realized this is a range for and therefore there is no iterators at play here at all (at least not explicitly). x is not an iterator and is actually a direct reference to an element of the container. But you have the right idea, it will be deduced to have the const qualifier regardless of whether one is explicitly written.
I'd like to offer a counter-point. In Herb Sutter's talk at CppCon 2014 "Back to the Basics! Essentials of Modern C++ Style" (timestamp 34:50), he shows this example:
void f( const vector<int>& v) {
vector<int>::iterator i = v.begin(); // error
vector<int>::const_iterator i = v.begin(); // ok + extra thinking
auto i = v.begin(); // ok default
He argues that auto will just work even if you change the parameter of the function, since it correctly deduces const or non-const. Further on slide 36, he states "You should know whether your variable is const/volatile or not!" as reasoning for why "auto&&" is bad for local variables.
Essentially it boils down to a matter of opinion. Some think that being explicit is good for readability or maintainability, but the opposite could be argued: being redundant shows a lack of understanding (either of C++ rules or what your code is actually doing1) and could harm readability and maintainability. Do whatever you think is best.
1: This is a rather contrived example, but C++'s rules of thumbs don't really work in the general case and are hard to remember. For example, Herb Sutter recommends you have constructor parameters pass by value contrary to regular function overloads. This is one of those situations where const everywhere could bite you in the foot, depending on whether or not you agree with Herb.
Mentioning const early and often tells the reader of the code more about what is going on without the reader having to look further away in the code.
Now, given your code, I'd write it as:
void print_book(const std::vector<Entry>& book)
{
for (auto&& x : book)
std::cout << x << '\n';
}
or even:
void print_book(std::experimental::array_view<const Entity> book)
{
for (auto&& x : book)
std::cout << x << '\n';
}
but only because the code is so short would I omit the redundant const.
(The array_view version removes the useless dependency on the argument being a vector -- vector<?> const& is usually over specifying your interface, as a vector<?> const& provides nothing useful that an array_view<?> does not provide, yet can force copies from initializer lists or from raw C arrays or the like.)
There is no reason to use const auto& over auto& or auto&& as far as the compiler is concerned, as all 3 (in this context) are deduced to be the same type. The only reason to use one or the other is to talk to other programmers.
In my case, I use auto&& by default (it says I am iterating, and don't really care what I'm iterating over).
auto& if I know I need to modify, and auto const& if I want to stress I am not modifying.
this specific example is quite simple, because input array is declared as const. In general, using const in ranged-based for can be useful for both developer (he will get compiler error if he tries to modify container) and for compiler (explicit const sometimes help it to optimize code)

const reference is bad C++ 11

I was watching Bjarne Stroustrup on YouTube and I was trying to figure out why this is considered bad as he said it is C++98 style bad code
void setInt(const unsigned int &i)
void takeaString(const std::string &str)
I mean you are passing a reference to a constant so you save yourself the copy operation and it isnt even using like passing the pointer so it doesnt have to dereference so why is it bad?
In pre-C++11, the general rule of thumb is if you don't modify the argument, pass a built-in type by value and an object of a class or struct by const&, because objects of classes or structs are typically so big that passing by const& pays in terms of performance.
Now that's a fairly arbitrary rule, and you'll also see exceptions, of course (e.g. iterators in the standard library) but it works well in practice and is an established idiom. When you see f(int const &i) or f(std::string s) in some other programmer's code, then you will want to know the reason, and if there's no apparent reason, people will be confused.
In C++11, the story may be different. A lot of people claim that due to new language features (move semantics and rvalue references), passing big objects by value is not a performance problem anymore and may even be faster. Look at this article: "Want Speed? Pass by Value." However, when you look at past Stack Overflow discussions, you will also find that there are experienced programmers opposed to this view.
Personally, I've not made up my mind on this. I consider C++11 too new for me to really judge what's good and bad.
Nevertheless, C++11 is often irrelevant if you have to use a pre-C++11 compiler for whatever reason, so it's important to know the pre-C++11 rules in any case.
Here is when it is good:
bool session_exists(heavy_key_t const& key)
{
// why would we ever want to copy the key if we don't need to
return sessions.find(key)) == sessions.end();
}
This is when passing argument by reference is possibly not so good:
struct session {
heavy_key_t key_;
session(heavy_key_t const& key):
key_(key) // <-- we are taking a copy anyway, why not letting compiler do it
{}
};
And another one that works just fine on values thanks to copy elision optimization and RVO:
template <class T, class Merger>
T merge(T state, T update, Merger const& merger) {
// merger is still by reference, we don't need the copy of it
return merger(std::move(state), std::move(update));
}

Qt - QList const correctness

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.

Why does operator ++ return a non-const value?

I have read Effective C++ 3rd Edition written by Scott Meyers.
Item 3 of the book, "Use const whenever possible", says if we want to prevent rvalues from being assigned to function's return value accidentally, the return type should be const.
For example, the increment function for iterator:
const iterator iterator::operator++(int) {
...
}
Then, some accidents is prevented.
iterator it;
// error in the following, same as primitive pointer
// I wanted to compare iterators
if (it++ = iterator()) {
...
}
However, iterators such as std::vector::iterator in GCC don't return const values.
vector<int> v;
v.begin()++ = v.begin(); // pass compiler check
Are there some reasons for this?
I'm pretty sure that this is because it would play havoc with rvalue references and any sort of decltype. Even though these features were not in C++03, they have been known to be coming.
More importantly, I don't believe that any Standard function returns const rvalues, it's probably something that wasn't considered until after the Standard was published. In addition, const rvalues are generally not considered to be the Right Thing To Do™. Not all uses of non-const member functions are invalid, and returning const rvalues is blanketly preventing them.
For example,
auto it = ++vec.begin();
is perfectly valid, and indeed, valid semantics, if not exactly desirable. Consider my class that offers method chains.
class ILikeMethodChains {
public:
int i;
ILikeMethodChains& SetSomeInt(int param) {
i = param;
return *this;
}
};
ILikeMethodChains func() { ... }
ILikeMethodChains var = func().SetSomeInt(1);
Should that be disallowed just because maybe, sometimes, we might call a function that doesn't make sense? No, of course not. Or how about "swaptimization"?
std::string func() { return "Hello World!"; }
std::string s;
func().swap(s);
This would be illegal if func() produced a const expression - but it's perfectly valid and indeed, assuming that std::string's implementation does not allocate any memory in the default constructor, both fast and legible/readable.
What you should realize is that the C++03 rvalue/lvalue rules frankly just don't make sense. They are, effectively, only part-baked, and the minimum required to disallow some blatant wrongs whilst allowing some possible rights. The C++0x rvalue rules are much saner and much more complete.
If it is non-const, I expect *(++it) to give me mutable access to the thing it represents.
However, dereferencing a const iterator yields only non-mutable access to the thing it represents. [edit: no, this is wrong too. I really give up now!]
This is the only reason I can think of.
As you rightly point out, the following is ill-formed because ++ on a primitive yields an rvalue (which can't be on the LHS):
int* p = 0;
(p++)++;
So there does seem to be something of an inconsistency in the language here.
EDIT: This is not really answering the question as pointed in the comments. I'll just leave the post here in the case it's useful anyhow...
I think this is pretty much a matter of syntax unification towards a better usable interface. When providing such member functions without differentiating the name and letting only the overload resolution mechanism determine the correct version you prevent (or at least try to) the programmer from making const related worries.
I know this might seem contradictory, in particular given your example. But if you think on most of the use cases it makes sense. Take an STL algorithm like std::equal. No matter whether your container is constant or not, you can always code something like bool e = std::equal(c.begin(), c.end(), c2.begin()) without having to think on the right version of begin and end.
This is the general approach in the STL. Remember of operator[]... Having in the mind that the containers are to be used with the algorithms, this is plausible. Although it's also noticeable that in some cases you might still need to define an iterator with a matching version (iterator or const_iterator).
Well, this is just what comes up to my mind right now. I'm not sure how convincing it is...
Side note: The correct way to use constant iterators is through the const_iterator typedef.