Warning for const in typedef - c++

When I was compiling a C++ program using icc 11, it gave this warning:
warning #21: type qualifiers are meaningless in this declaration
typedef const direction_vector_ref_t direction_vector_cref_t;
It is saying const just meaningless. I am curious about this since if this typedef expands it will turn into const array<double,3>& and the const is definitely meaningful. Why it gave this warning?

direction_vector_ref_t, I pressume, its a reference. References are const by design, so adding const to a reference is meaningless. What you probably want is to make a reference to a const object, which can't be done by a typedef. You will have to repeat the slightly modified typedef definition.
Just to clarify:
typedef T& ref;
typedef const T& cref;
typedef const ref cref;
The last line is the same as the first one, not the second one. Typedefs are not token pasting, once you typedef T& as ref, then ref refers to the reference to T type. If you add const to it, then you get a const reference to T type, not a reference to a const T type.

Are you sure? Try:
array<double, 3> a;
direction_vector_cref_t b = a;
b[0] = 1.0;
The issue here, is that when you use a typedef, it conceptually adds parentheses around the type, so you are conceptually using const (array<double, 3>&) as opposed to (const array<double, 3>)&, so you are not actually making the referent object constant. So your declaration is more like:
typedef array<double, 3>& const direction_vector_cref_t;
And in the above, the const for the variable (rather than the referent type) needs to be deferred till later.

Related

Shouldn't "const references" be actually called "references to const"?

I came up with this feeling when playing with constexpr references. But the issue itself is not related to constexpr, it is merely revealed by it.
We know that there are "pointers to const" and there are "const pointers". BTW, since the latter are much less used than the former, their name is often actually used to reference the former. Anyway, there is no this distinction for references, since they are not rebindable. Thus we only have "const references" and not "references to const". This is the common terminology and it is used in the standard as well. But it does not seem right to me.
Let's take a look at some well-known examples, commented with this common terminology:
int i = 0; // Value
int* pi = &i; // Pointer to i
const int* cpi = &i; // Pointer to const i
int const* cpi = &i; // Pointer to const i (east-const style)
int* const pci = &i; // Const pointer to i
int& ri = i; // Reference to i
const int& rci = i; // Const reference to i
int const& rci = i; // Const reference to i (east-const style)
Now let's take a closer look on several lines from the above:
const int* cpi = &i; // Pointer to const i
const int& rci = i; // Const reference to i
const int* declares "pointer to const", while const int& declares "const reference". Same order in the declaration, different order in the meaning. There is a clear inconsistency here. But if we call it "reference to const" inconsistency is gone. Same order on both sides.
The tension intensifies with the introduction of constexpr:
constexpr int& cri = i; // Constexpr reference to i
constexpr const int& crci = i; // Constexpr const reference to i
"Constexpr const" reference? Huh? The documentation clearly says:
A constexpr specifier used in an object declaration implies const.
A reference is an object. So why would we need const when it is already constepxr?.. Yet we need it, if i is const. Looks frustrating.
But the frustration is no more with help of "reference to const". Let's call crci "constexpr reference to const i". Now it is clear that const is applied to the referenced object, while constexpr is applied to the reference itself, stating the fact it can be used in constexpr context. And also that it is const, since all references are const. Makes perfect sense. To make it more consistent with the pointers, we might even have used the following hypothetical syntax:
const int& constexpr crci = i; // Constexpr reference to const i
const int* const pci = &i; // Const pointer to const i (perfect consistency)
But that's not how C++ works, for better or worse.
This concept of "reference to const" really helped me to understand "constexpr const reference" thing.
What do you think? Does the concept of "reference to const" sound convincing to you?
Yes, the proper name for const int & is a "reference to const int".
It's often called a "const reference to int" (probably because it sounds better to some), but technically that's a misnomer.
Even though they are not rebindable, references themselves are never const (i.e. std::is_const_v always equals to false for a reference).
constexpr const int& crci
"Constexpr const" reference? Huh? ... Let's call crci "constexpr reference to const i"
That's correct, crci is a "constexpr reference to const i".
To make it more consistent with the pointers, we might even have used the following hypothetical syntax:
const int& constexpr crci = i; // Constexpr reference to const i
const int* const pci = &i; // Const pointer to const i (perfect consistency)
To be honest, I don't see how this proposal improves consistency. int *constexpr pci; is not allowed for pointers, so why should it be allowed for references?
Things seem to be reasonably consistent to begin with:
constexpr const int *a; // constexpr (and const) pointer to const int
constexpr const int &a; // constexpr (but not const) reference to const int
Yes, adding constexpr doesn't make a reference itself const, but I don't think it needs to be fixed. (What would be the difference between a const refenrece and a non-const reference?)
Note that constexpr is not a part of a type, but it's a property of a variable. (e.g. constexpr int x; declares x with the type const int, and not some hypothetical constexpr int).
Since volatile tends to be also be relevant, the standard often uses "reference to a cv-(un)qualified" in normative text to cover both qualifiers, which conforms to your preference.
With quick search, besides the code example that you quoted, the standard appears to use (non-) const reference in following places, all in the standard library spec:
[container.node.observers]
key_type& key() const;
Returns: A non-const reference to ...
[rand.req.adapt]
... The expression a.base() shall be valid and shall return a const reference ...
[futures.shared.future
— (19.1) shared_future::get() returns a const reference to ...
Arguably, the wording could be improved by using reference to const.
Outside of the standard, "references to const" is used. But so is const reference, which is sometimes chosen possibly due to terseness.
In fact, pointers to const are also colloquially often called const pointers even though that meaning is ambiguous, which easily leads to confusion. In case of references, there is no ambiguity to those who know that references cannot be top level qualified. Beginners won't know that, so the use of "reference to const" is important in introductory material in my opinion. But so is perhaps explanation that const reference is used to mean the same thing.

Why is the const qualifier in this type alias dropped? [duplicate]

This question already has answers here:
constant references with typedef and templates in c++
(5 answers)
Closed 4 years ago.
TL;DR
Given the following type:
struct A
{
std::vector<std::string> vec;
using reference = std::iterator_traits<decltype(vec)::iterator>::reference;
using const_reference = const reference;
};
Why is reference == const_reference? Why is the const qualifier dropped in the second type alias?
See the example on godbold which shouldn't compile.
Details
I have a templated class that takes a bunch of iterators (-types) as template arguments. From these iterators I need to deduce the reference and const reference type because I have some member functions like:
struct A
{
std::vector<std::string> vec;
using reference = std::iterator_traits<decltype(vec)::iterator>::reference;
using const_reference = const reference;
const_reference foo() const
{
return vec[0];
}
};
By dropping the const qualifier, I'm effectively returning a reference in foo which is illegal because it's a const member function and so the compiler throws.
It is dropped. What we call a "const reference" is really a reference to const - const int&.
Having an int& and adding const would make that int& const. But such a const is dropped.
Your problem is similar to the difference between const int* and int* const. Except that for references, int& and int& const is the same type - the const is ignored.
Your problem is west const. West const is bad const east const is best const.
West const is putting the const on the left of the token you want to be const. East const is putting it on the right.
If I told you never to put const on the left of your types, and that const always applies to the thing on its left, look at this:
using const_reference = reference const;
you can probably work out why the const didn't work. After expanding reference naively you get string&const -- here you attempt to apply const to & not string, and a foo &const is not the same as a foo const& -- a foo&const is just a foo& as const cannot apply to the reference itself, but only to the type referred to.
Sure, you say, but that is why I want west const!
West const does the same thing here. It applies to the & not then string and then is discarded. It just does so in a way that is more confusing and harder to grasp intuitively.
To understand const, convert your const to east const. West const is just an exception to the standard east const rule, where if there is no token in the type to the left it applies to the token on the right. The token on the right here is the entire type string& bundled into a type alias (type aliases are not macros). If instead you typed const string& the token on the right would be string and you'd get string const& in sane, east-const style.
East const is best const.

Cannot apply const to typedef reference

The following code works when applying const to a return value reference of value_type& but errors if I use a typedef of the same type.
As an example:
class T {
};
class A {
public:
typedef T value_type;
typedef value_type& reference;
// Not working
const reference operator*() const;
// But this works?
//const value_type& operator*() const;
};
// Error!
const typename A::reference A::operator*() const {
}
int main() {
return 0;
}
g++ will error with:
'const' qualifiers cannot be applied
My actual code uses templates but I've removed for the example and substituted class T instead. This has no bearing on the error.
I don't see why this won't work if specifying value_type& instead compiles fine.
There are two different issues here.
First, in:
typedef T* pointer;
typedef const pointer const_pointer;
the type of const_pointer is actually T* const, not const T*. The constness attaches to the pointer, not to the type pointed to.
Now references obey the same logic: if you make a typedef for the reference type, and try to attach const to it, it will try to apply the const to the reference type, not the referenced type. But references, unlike pointers, are not allowed to have top-level cv-qualification. If you try to mention T& const, it's a compilation error. But if you try to attach the cv-qualification through a typedef, it's just ignored.
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced
through the use of a typedef-name (7.1.3, 14.1) or *decltype-specifier *(7.1.6.2), in which case the cv-qualifiers
are ignored.
([dcl.ref]/1)
So the second issue is that GCC thinks this is an error, whereas the standard clearly states that it should not be an error, it should just ignore the const. I think this is a bug in GCC. Clang does not produce an error: http://coliru.stacked-crooked.com/a/5b5c105941066708
You cannot apply const to a reference, like const(ref(type)).
But you can have a reference to a const type, like ref(const(type)).

Template neglects const (why?)

Does somebody know, why this compiles??
template< typename TBufferTypeFront, typename TBufferTypeBack = TBufferTypeFront>
class FrontBackBuffer{
public:
FrontBackBuffer(
const TBufferTypeFront front,
const TBufferTypeBack back): ////const reference assigned to reference???
m_Front(front),
m_Back(back)
{
};
~FrontBackBuffer()
{};
TBufferTypeFront m_Front; ///< The front buffer
TBufferTypeBack m_Back; ///< The back buffer
};
int main(){
int b;
int a;
FrontBackBuffer<int&,int&> buffer(a,b); //
buffer.m_Back = 33;
buffer.m_Front = 55;
}
I compile with GCC 4.4. Why does it even let me compile this? Shouldn't there be an error that I cannot assign a const reference to a non-const reference?
The thing is that if type T is int&, then the type const T is not const int&, but int & const. The illegal top-level const on a reference is ignored in template substitutions and typedef results.
If, on the other hand T is const int, then T& is const int&
When TypeBufferFront is int&, const TBufferTypeFront is equivalent to int& const, where the const is ignored during template substitution, since all references are constant, even if what they refer to is not.
So, when instantiated with int&, your constructor is effectively FrontBackBuffer(int&, int&), which works as given.
This is an example of why many people will use T const instead of const T, to make it clearer how the substitution occurs, as well as allow them to read the cv-qualifiers from right to left.
For the code to do what you want it to do, it would have to read:
FrontBackBuffer(
typename std::remove_reference<TBufferTypeFront>::type const& m_front,
typename std::remove_reference<TBufferTypeBack>::type const& m_back): ////const reference assigned to reference???
m_Front(m_front),
m_Back(m_back)
{
};
which has the added "feature" that it turns other types into const references when used to construct FrontBackBuffer.
Now this isn't perfect. This prevents temporary arguments to FrontBackBuffer from being moved, and passes even small cheap to copy types (like char) by reference instead of by value. There are standard C++0x techniques to do this that are a bit awkward to write if you care.
FrontBackBuffer::m_Front is of type TBufferTypeFront which translates to int& in your template instantiation. There is nothing wrong with assigning to an int&.

constant references with typedef and templates in c++

I heard the temporary objects can only be assigned to constant references.
But this code gives error
#include <iostream.h>
template<class t>
t const& check(){
return t(); //return a temporary object
}
int main(int argc, char** argv){
const int &resCheck = check<int>(); /* fine */
typedef int& ref;
const ref error = check<int>(); / *error */
return 0;
}
The error that is get is invalid initialization of reference of type 'int&' from expression of type 'const int'
This:
typedef int& ref;
const ref error;
Doesn't do what you think it does. Consider instead:
typedef int* pointer;
typedef const pointer const_pointer;
The type of const_pointer is int* const, not const int *. That is, when you say const T you're saying "make a type where T is immutable"; so in the previous example, the pointer (not the pointee) is made immutable.
References cannot be made const or volatile. This:
int& const x;
is meaningless, so adding cv-qualifiers to references has no effect.
Therefore, error has the type int&. You cannot assign a const int& to it.
There are other problems in your code. For example, this is certainly wrong:
template<class t>
t const& check()
{
return t(); //return a temporary object
}
What you're doing here is returning a reference to a temporary object which ends its lifetime when the function returns. That is, you get undefined behavior if you use it because there is no object at the referand. This is no better than:
template<class t>
t const& check()
{
T x = T();
return x; // return a local...bang you're dead
}
A better test would be:
template<class T>
T check()
{
return T();
}
The return value of the function is a temporary, so you can still test that you can indeed bind temporaries to constant references.
Your code gives error because the const qualifier in const ref error is just ignored because 8.3.2/1 says
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef (7.1.3) or of a template type argument(14.3), in which case the cv-qualifiers are ignored.`
So error has type int& not const int& .
It's a very common mistake for English speaking people, because of the way the English grammar works.
I consider it extremely unfortunate that the C++ syntax would allow both:
const int // immutable int
int const // immutable int
to have the same meaning.
It doesn't make it easier, really, and isn't composable since:
const int* // mutable pointer to immutable int
int* const // immutable pointer to mutable int
certainly do NOT have the same meaning.
And this, unfortunately for you, what kicks in here, as #GMan explains.
If you wish to avoid this kind of error in the future, take the habit of qualifying your types (const and volatile) on their right, then you'll be able to treat a typedef as simple text replacement.
To maintain consistency with the Right Left Rule, I prefer to use 'cv' qualifiers like so.
int const x = 2; // x is a const int (by applying Right Left rule)
int const *p = &x; // p is a pinter to const int
In your example, I would write const ref error = check<int>(); like so
ref const error = check<int>(); // parsed as error is a const reference to an integer
As #Prasoon Saurav pointed out, cv qualifiers are ignored when introduced through typedef because as #GMan also says, that cv qualified references are ill-formed.
Therefore the declaration is effectively as below, which of course is an error.
int &error = check<int>();
Check out this for more information.
This is compiled:
typedef const int& ref;
ref error = check<int>();
VC++ compiler gives some explanation of your error: qualifier applied to reference type; ignored. Reference type must be declared as constant, const cannot be applied later.