CV-qualified reference - c++

8.3.2/1:
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-specificer (7.1.6.2), in which case the cv-qualifiers are
ignored
int a = 5;
const int &b = a;
int main()
{
}
Compiles fine by both gcc and clang. DEMO
Why? Is it a bug?
Example, provided by the Standard:
typedef int& A;
const A aref = 3; // ill-formed; lvalue reference to non-const initialized with rvalue

In your example you have no cv-qualified reference. The quote of the Standard means the following declarations that are ill formed
int a = 5;
const int & const b = a;
Here b is a cv-qualified reference and the code snippet is ill-formed.
As for your code snippet then it declares a reference to a const object.
This code snippet is ill formed
typedef int& A;
const A aref = 3;
because it is equivalent to
int & const aref = 3;
That it would be more clear compare the two declarations
int x;
int * const p = &x;
int & const r = x;
The declaration of the cv-qualified pointer is valid while the declaration of the cv-qualified reference is ill-formed.

Related

What types of objects can constexpr pointers point to?

cppreference.com says:
A constexpr specifier used in an object declaration implies const.
But I tried to make a constexpr pointer hold the address of a const object of the same base-type but the compiler gave me an error:
const int a = 1;
int main(){
constexpr int *b = &a;
return 0;
}
So, what types can a constexpr pointer point to?
The issue here is not with constexpr. If you said
int *b = &a;
You'd get the same error. i.e. "invalid conversion from const int* to int*"
We can fix that by making it a pointer to a const int.
int const *b = &a;
Now we can add constexpr, and yes, constexpr does imply const
constexpr int const *b = &a;
where b is in fact const. This is exactly the same as the following
constexpr int const * const b = &a;
//^^^^^
// this const is made redundant by the constexpr.
Your example doesn't compile because 'a' is a 'const int', and requires a 'constexpr const int' pointer to point to it.

pointer to function as static member in structure

struct X {
int f(int);
static int f(long);
};
int (X::*p1)(int) = &X::f; // OK
int (*p2)(int) = &X::f; // error: mismatch
int (*p3)(long) = &X::f; // OK
int (X::*p4)(long) = &X::f; // error: mismatch
int (X::*p5)(int) = &(X::f); // error: wrong syntax for pointer to member
int (*p6)(long) = &(X::f); // OK
I think that p1 and p5 is the same case. Why is p5 wrong?
Because the standard says so. From the N3936:
5.3.1 Unary operators
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [ Note:
that is, the expression &(qualified-id), where the qualified-id is
enclosed in parentheses, does not form an expression of type “pointer
to member.” Neither does qualified-id, because there is no implicit
conversion from a qualified-id for a non-static member function to the
type “pointer to member function” as there is from an lvalue of
function type to the type “pointer to function” (4.3). Nor is
&unqualified-id a pointer to member, even within the scope of the
unqualified-id’s class. — end note ]
The C++ Standard's definition of the built-in operator & states that only when the parameter to & is a qualified-id, meaning something like Class::Member, does & result in a pointer-to-member. The parentheses make it no longer a qualified-id, so it attempts to parse X::f directly, which is illegal in this context: you're assigning an int (*)(long) to an int (X::*)(int).
The distinction between the two cases resolves an ambiguity. Let's say that you have:
struct X {
int m;
};
struct Y {
int m;
};
struct Z : X, Y {
void F();
};
void Z::F() {
int X::*p1 = &X::m;
int *p2 = &(X::m);
}
Here, &X::m is a pointer-to-member, whereas &(X::m) is an ordinary pointer to int, using the X:: qualification to resolve the ambiguity between X's m and Y's m.

clarification of decltype output when multiplying 2 const ints

int main()
{
const int a = 1;
const int b = 2;
typedef decltype(a*b) multiply_type;
cout << typeid(multiply_type).name() << endl;
return 0;
}
The return value of the program is that multiply_type is int. I'm quite surprised. I expected the type deduction to yield const int and since the expression yields a pr value, the resultant type would be const int.
PS: With auto the return value would be int as it drops the const qualifier.
Any ideas why multiply_type is int instead of const int with decltype ?
Edit: Added an addition example which is also related to cv-qualifier.
#include<iostream>
#include<typeinfo>
using namespace std;
struct Details
{
int m_age;
};
int main()
{
const Details* detail = new Details();
typedef decltype((detail->m_age)) age_type;
cout << typeid(age_type).name() << endl;
int a = 1;
age_type age = a;
age = 10; // This is not possible. Read only.
cout << typeid(age).name() << endl; // This returns the type as int though. Then why is 20 not possble ?
return 0;
}
Edit 2: Check our the link.
http://thbecker.net/articles/auto_and_decltype/section_07.html
`
int x;
const int& crx = x;
/ The type of (cx) is const int. Since (cx) is an lvalue,
// decltype adds a reference to that: cx_with_parens_type
// is const int&.
typedef decltype((cx)) cx_with_parens_type;`
decltype evaluate it argument as it is, decltype(i) where i is cv-qualified lvalue, results in declaring type cv-qualified, but the expression of i*i in decltype(i*i) create a non materialized prvalue with type of i with non cv-qualified, prvalue don't have an explicit notion of constness. your code produce the same as:
using T = const int;
static_assert(is_same<int, decltype(0)>(), "Failed");
The fact that typeid is not showing the cv-qualification is because they a ignored:
5.2.8.5 - If the type of the expression or type-id is a cv-qualified type, the result of the typeid expression refers to a std::type_info object representing the cv-unqualified type.

c++ "const T &const" What is the meaning of the 2nd const?

Could you please explain me the meaning of the second "const" in the following expression:
int i = 42;
const int &const ri = i;
Are there some cases where const T &const is mandatory?
What I understood so far:
Pointers are objects, and they can be reassigned.
int i = 42;
const int *ptr1 = &i; // valid: (low-level const): *ptr1 cannot be changed.
int *const ptr2 = &i; // valid: (high-level const): ptr2 cannot be changed.
const int *const ptr3 = &i: // also valid (combination of 2 precedent lines).
References, unliked pointers are not objects (they have no address) and cannot be reassigned ==> no meaning for an "high-level const"
int i = 42;
int &ri1 = i; // valid: ri1 is a new name for i
const int &ri2 = 7; // valid and const is required
const int &ri3 = i; // valid, what is the use of const?
const int &const ri4 = i; // valid, has the second const an importance?
The form int & const is ill-formed (C++11 §8.3.2):
In a declaration T D where D has either of the forms
& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1
and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T,” then the type of the identifier of D is “derived-declarator-type-list reference to T.” The optional attribute-specifier-seq appertains to the reference type. 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 no, it can't be mandatory anywhere. And it has no semantics.

Why can I create cast operator to every class except self

struct B
{
};
struct A
{
operator A&() const;
operator B&() const;
};
int main()
{
const A a;
B& br = a;
A& ar = a;
}
Why can I create cast operator to B&, but not to A&.
May be it does not have much sense (one can use it to erase const modifier, as in example), but it at least inconsistent!
You can't do this because it's explicitly forbidden. N3290 § 12.3.2 states:
Such functions are called
conversion functions. No return type can be specified. If a conversion function is a member function, the
type of the conversion function (8.3.5) is “function taking no parameter returning conversion-type-id”. A
conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified)
same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to
it), or to (possibly cv-qualified) void.
(Emphasis mine)
This is discussed further in a note:
These conversions are considered as standard conversions for the purposes of overload resolution (13.3.3.1, 13.3.3.1.4) and
therefore initialization (8.5) and explicit casts (5.2.9).
Which explains this decision - it would interfere with the built-in mechanics too much. (For little gain).
If you really want something non-const from a const object the only smart way to do this is constructing a new instance using the copy constructor.
As a work around you could introduce a lightweight intermediary (like a smart pointer):
struct B {};
struct A {};
namespace {
B b_inst;
A a_inst;
}
struct A_wrapper {
A& inst;
// This is perfectly fine: const alters the reference, not what it refers to
operator A&() const { return inst; }
operator B&() const { return b_inst; }
A_wrapper() : inst(a_inst) {}
};
int main() {
const A_wrapper a;
B& br = a;
A& ar = a;
}
But really, wanting to do this in the first place looks like a code smell.
The proper way to do this would be to use const_cast.
For example,
#include <iostream>
using namespace std;
void f(int* p) {
cout << *p << endl;
}
int main(void) {
const int a = 10;
const int* b = &a;
// Function f() expects int*, not const int*
// f(b);
int* c = const_cast<int*>(b);
f(c);
// Lvalue is const
// *b = 20;
// Undefined behavior
// *c = 30;
int a1 = 40;
const int* b1 = &a1;
int* c1 = const_cast<int*>(b1);
// Integer a1, the object referred to by c1, has
// not been declared const
*c1 = 50;
return 0;
}
Declaring a conversion to a reference to self is not ill-formed. Your problem comes at the time where your reference is initialized. As the type of the reference and the type of the initialization expression are the same, the reference is bound directly and your user defined conversion operator is never considered. Thus normal conversion rules apply and const conversion makes the code ill-formed.
Anyway, what your are doing is basically asking yourself to get shot in the foot. If you don't like constness, don't use it. If you do it consistently, it will never bother you, but it is not going to make you new friends.