error in attaching temporary to a reference to const [duplicate] - c++

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
typedef and containers of const pointers
Why is the code emitting an error?
int main()
{
//test code
typedef int& Ref_to_int;
const Ref_to_int ref = 10;
}
The error is:
error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘int’
I read the post on prolonging the lifetime of temporaries which says that temporaries can be bound to references to const. Then why is my code not getting compiled?

Here the type of ref is actually reference to int and not const reference to int. The const qualifier is ignored.
$8.3.2 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.
const Ref_to_int ref; is equivalent to int& const ref; and not const int& ref.

Mixing const with a typedef doesn't work the way you're thinking; see this question for more info. These two lines are equivalent:
const Ref_to_int ref;
int& const ref;
You're looking for:
const int& ref;
One way to fix it is to include it in the typedef itself (although you should probably rename it then):
typedef const int& Ref_to_int;

You can't add additional specifiers to a typedef. It doesn't work like a macro.
Your code is effectively
int& const ref = 10; // error
which isn't valid.

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.

Binding rvalue to const lvalue reference [duplicate]

This question already has answers here:
Why not non-const reference to temporary objects? [duplicate]
(4 answers)
Closed 6 months ago.
For some reason I didn't manage to find this exact question. Why is it allowed to bind an rvalue to const lvalue reference, although it is impossible to to the same without the const?
I do understand that the lifetime of the rvalue gets an extension somehow (in the first case) , but if so, why would the compiler disallow changing that 'rvalue', which is not really a temporary object anymore.
For example, consider the following code:
int main(){
int &i=3; //produces error
const int&j =3; //compiles
return 1;
}
You may find the following article useful:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0345.pdf
I might be entirely wrong here, but this is how I rationalise it. An rvalue is constant, it cannot be changed. you cannot change the integer 5, fact. So when you bind the references the lvalue will have to be const. Otherwise your compiler will throw an error:
obj & a1 = bar();
invalid initialization of non-const reference of type ‘obj&’ from an rvalue of type ‘obj’
using g++
The only way to safely bind an rvalue to an lvalue is either by marking the lvalue as const, or using a mutable rvalue reference && (introduced in C++11 believe?)
struct obj {
};
obj bar() {
obj x;
return x;
}
int main() {
const obj & a1 = bar();
obj && a2 = bar();
return 0;
};
If you're asking about
void f(const int&);
f(1);
versus
void g(int&);
g(1);
the answer is to think about what happens if g looks like this:
void g(int& r) {
++r;
}
The name r-value comes from the right-value: which (roughly) means things that are on the right side of a x=y kind of statement. It is like: it can be read from, but it may not be suitable to be written on. For example a numeric literal can be on the right side (a=3) but it does not make sense to be on the left side (3=a).
With this reasoning, it looks logical to me why it is allowed to make const l-value references to r-values but not allowed to make non-const references.

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)).

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.