Why const is ignored in auto keyword on constant reference - c++

int main()
{
int a = 10;
const int &b = a;
int &c = b; //gives error : C should a reference to a const b
auto d = b; // why const is ignored here?
d = 15;
cout << a << d;
}
In c++ Primer, it's mentioned that "const in reference type is always low-level " Then how come auto d = b is not constant?

Because the type deduction rules for auto deduce to an object type. You are copy initializing a new int from the one referenced by b.
That's by design. We want to create new objects without explicitly specifying their type. And more often than not, it's a new object that is a copy some other object. Had it deduced to a reference, it would defeat the intended purpose.
If you want the deduced type to be a reference when the initializer is a reference, then that is accomplished with a placeholder of decltype(auto):
decltype(auto) d = b; // d and b now refer to the same object
d = 15; // And this will error because the cv-qualifiers are the same as b's

For auto d = b, you're declaring d as non-reference. That means d will be a new object copied from b, then the reference part of b will be ignored, after that, the const-ness is ignored too. So the type of d is int.
You can declare d as reference explicitly, then the const-ness of b won't be ignored. e.g.
auto& d = b; // the type of d is const int &

Because the deducted type is as an int, not an int&.
That means d is not a reference.

Related

Reference initialization with constructor

const T a {}; // constant of type T
const T& b {}; // ???
T c {}; // variable of type T
T& d {}; // error
What is the difference between a and b?
b is a reference but I don't assign a object to it: in that instruction I initialize it by T constructor.
The address of b is between the addresses of a and c, so it seems the b and a have no differences.
And if I can declare and initialize b why d gives compilation error?
I talked about a generic type T. I tested the code above either for primitive types and classes and the results are the same.
In these declarations
const T a {}; // constant of type T
const T& b {};
there is created constant object a that is default initialized and constant reference b to temporary object that is default initialized.
The compiler issues an error for this declaration of a reference
T& d {}; // error
because there is declared a non-constant reference to a temporary object.
You could declare an rvalue reference the following way
T&& d {};
Here is a demonstrative program
#include <iostream>
int main()
{
const int &ri {};
std::cout << "ri = " << ri << '\n';
int && rri {};
std::cout << "rri = " << rri << '\n';
return 0;
}
The program output is
ri = 0
rri = 0

type deduction fail: shared_ptr of derived template to base template as function argument

Here I declared two template classes: A and B, B derives from A:
template<typename T>
class A {
public:
int a;
T t;
};
template<typename T>
class B : public A<T> {
public:
int b;
};
And I make a shared_ptr<B<T>> and assign it to shared_ptr<A<T>>, it's ok:
auto b = std::make_shared<B<std::string>>();
std::shared_ptr<A<std::string>> a = b;
Here I declared a template function accept shared_ptr A<T>:
template<typename T>
void proc(std::shared_ptr<A<T>> &a) {
std::cout << a->a << std::endl;
}
it accepts a as argument, but rejects b:
proc<std::string>(a); // OK
proc<std::string>(b); // template argument deduction/substitution failed
// cannot convert 'b' (type 'std::shared_ptr<B<std::__cxx11::basic_string<char> > >') to type 'std::shared_ptr<A<std::__cxx11::basic_string<char> > >&'
I use g++ as compiler with -std=c++11.
This error brings me a lot problems and how could I fix this elegantly?
Given proc<std::string>(b);, b needs to be converted to std::shared_ptr<A<std::string>>. That means a temporary std::shared_ptr<A<std::string>> will be constructed and then passed to proc. The parameter type of proc is an lvalue-reference to non-const, i.e. std::shared_ptr<A<T>> &, which can't bind to temporaries.
You can change the parameter type to lvalue-reference to const, which could bind to temporaries. e.g.
template<typename T>
void proc(const std::shared_ptr<A<T>> &a) {
// ^^^^^
std::cout << a->a << std::endl;
}
First of all, you make a shared_ptr called:
auto b = std::make_shared<B<std::string>>();
Is of type std::shared_ptr<B<std::string>> and,
std::shared_ptr<A<std::string>> a = b;
Is of type std::shared_ptr<A<std::string>>...
In your function parameter, however, you have:
void proc(std::shared_ptr<A<T>> &a)
Which only points to the shared_ptr of A, not B, so it is obvious that B won't become A...
The solution would be to remove the lvalue reference of a from the function definition, like:
void proc(std::shared_ptr<A<T>> a)
so, it doesn't refer to A, and B can easily be converted to A during function call...
Edit: Added an explanation...
Explanation:
Remember pointers? from C... yes, they do the same function of references:
// Compilable both in C and C++...
int add(int a, int b, int * more_than_3) {
int const result = a + b;
if (result > 3)
*more_than_3 = 1;
return result;
}
Yeah, these which would the function of pseudo-return types in C. Like:
// Compilable both in C and C++...
int main(void) {
int more_3;
int const res = add(2, 3, &more_3);
printf("Sum of 2 + 3 is %i\n", res);
if (more_3)
printf("Given two numbers' sum is more than 3");
}
Here, an extra argument is passed which takes the address of a variable (References also do the same thing, they share their address with the variable with whom they are referenced...)
Remember, references and pointers store the address of another variable inside of them...
This might be the reason why they made the address of operator (&) also act for references in C++...
Also, unneeded, but the answer which was posted here by #songyuanyao worked, because:
void proc(std::shared_ptr<A<T>> const &a)
uses a constant reference, a reference to a constant expression, not a variable, so it didn't matter if they mismatched (A and B)

Compound types, const and auto in C++

I'm trying to understand this piece of code. I'm stuck at figuring out why d and e are int* and const int*. I could use some help.
const int ci = i, &cr = ci;
auto b = ci; // b is an int (top-level const in ci is dropped)
auto c = cr; // c is an int (cr is an alias for ci whose const is top-level)
auto d = &i; // d is an int*(& of an int object is int*)
auto e = &ci; // e is const int*(& of a const object is low-level const)
&i means "take the address of i". Since i is a int, the type of &i is int*. The type of d is deduced as int* due to automatic type deduction rules.
The same reasoning can be applied to ci. The only difference is the const qualifier.

How class constructor works in C++

I saw an online C++ test regarding the constructor. I can figure out most of the answers but am puzzled by some in the following. Hope someone can help me out.
Here's the example.
#include <iostream>
class A {
public:
A(int n = 0) : m_n(n) {
std::cout << 'd';
}
A(const A& a) : m_n(a.m_n) {
std::cout << 'c';
}
private:
int m_n;
};
void f(const A &a1, const A &a2 = A())
{
}
int main() {
A a(2), b;
const A c(a), &d = c, e = b;
b = d;
A *p = new A(c), *q = &a;
static_cast<void>(q);
delete p;
f(3);
std::cout << std::endl;
return 0;
}
What I don't really get is why "&d = c" doesn't output anything. Also adding another overloading constructor like A(const A *a) : m_n(a->m_n) { std::cout << 'b'; } doesn't output anything either for *q = &a. So what can I do to make it work?
Many thanks for any advice. I am very curious about this.
There's no output for these because d and q are not of type A, i.e. they are not A objects. d is a reference to A and q is a pointer to A. Initialising a reference and initialising or assigning a pointer does not manipulate the referred-to/pointed-to A object at all, hence no output.
To address your question - there is nothing to "make work," it works just as it should.
That to be more clear I will rewrite this statement
const A c(a), &d = c, e = b;
as
const A c(a);
const A &d = c;
Here d is declared as a reference to an object of type A. It does not create a new object. It refers to an object that is already created. In this case d referes to c. In fact d is simply an alias for object c.
This code snippet
A *p = new A(c), *q = &a;
also can be rewritten for simplicity
A *q = &a;
In this statement pointer q is simply assigned by the address of a. Neither object is created. Simply q now points to already created early object a.
&d = c doesn't output anything because you're not calling the constructor.
If we expand that code fragment a bit...
A &d = c
What your code is saying there is "declare d to be a reference to an object of type A, which points to c". Because you're creating a reference, you're not calling the constructor, c and d are the same object.
The same applies to q, but instead of creating a reference, you're creating a pointer and assigning it the address of an existing instance of type A. The constructor isn't called because you're not creating a separate object, you're linking to an existing one.

Can someone explain ref( ) vs & in c++11 and when to use each?

I read the documentation on it but if someone could explain it a little more plain english I would appreciate it.
Plain C++ reference & is an odd citizen, because it can be initialized but not re-assigned. E.g.:
int a, &ra = a;
int b, &rb = b;
ra = rb; // actually does a = b
Whereas plain pointer is a well-behaved citizen, it can be both initialized and re-assigned.
Hence ref() creates reference_wrapper which is a wrapper over a plain pointer. This wrapper can be initialized with a reference and it has an automatic conversion to plain reference &, e.g.:
int a;
auto ra = std::ref(a);
int b;
auto rb = std::ref(b);
ra = rb; // now ra contains a pointer to b
int& rb2 = ra; // automatically converts to reference
It is mainly useful for functional style programming with lambdas or std::bind expressions. std::bind copies bound arguments, so if you'd like to bind a function argument to a reference reference_wrapper comes handy. E.g.:
void foo(int);
int i = 1;
auto f = std::bind(foo, i); // makes a copy of i
i = 2;
f(); // calls foo(1)
auto g = std::bind(foo, std::ref(i));
i = 3;
g(); // calls foo(3);