How C++ do overload resolution for this "vector[0] = 1;" - c++

As I know, C++ only have function overload base on the parameter or implicate object parameter. But I find there are two operator[] for vector. And it will select the correct function in following code:
std::vector<int> v;
v[0] = 1; // This will select the non-const version.
return &v[0]; // This will select the const version.
Can anyone explain how this happen?
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;
------Edit 1------
I think it will select the const version, because following cc file cannot compile with clang++ and g++ with following error. Doesn't understand following error. Can anyone explain more?
error: cannot initialize return object of type 'char *' with an rvalue
of type
'const value_type *' (aka 'const char *')
return data_.size() == 0 ? NULL : (&data_[0]);
#include <assert.h>
#include <deque>
#include <vector>
#include <map>
class X
{
public:
X() {
}
virtual ~X() {
}
char* data() const {
return data_.size() == 0 ? NULL : (&data_[0]);
}
size_t size() const {
return data_.size();
}
private:
std::vector<char> data_;
};

Actually in both cases the non-const version is called. The time when the const version would be called would be if the vector was const.
std::vector<int> const v = {1,2,3};
int x = v[0];
In the above case, trying to invoke the non-const version would result in a compiler error
v[0] = 5; // nope can't call non-const version, this is trying to mutate a const variable
Edit
Regarding your example, based on the signature of your function
char* data() const
you have declared that the method data is const, meaning that it shall not attempt to mutate any of the member variables. In other words, all member variables within a const function are treated as const. In the context of a const method, the variable is seen as
std::vector<char> const data_;

Since v is a non-const vector the constversion is never called.

Related

C++ Compilation error in overloading

The following code compiles fine.
#include <iostream>
#include <vector>
using namespace std;
class MyClass
{
public:
MyClass()
{
x.resize(2);
x[0] = 10;
x[1] = 100;
}
std::vector<int> getValue()
{
return x;
}
const std::vector<int>& getValue() const
{
return x;
}
private:
std::vector<int> x;
};
int main()
{
MyClass m;
std::vector<int> y = m.getValue();
for(int i=0; i<y.size(); i++)
{
std::cout<<y[i]<<std::endl;
}
const std::vector<int>& z = m.getValue();
for(int i=0; i<z.size(); i++)
{
std::cout<<z[i]<<std::endl;
}
return 0;
}
However, when I change the "std::vector getValue()" to a more correct version (since the function is supposed to change the object) by adding "const" (std::vector getValue() const) it gives the following compilation error.
error: 'const std::vector<int>& MyClass::getValue() const' cannot be overloaded const std::vector<int>& getValue() const
Why is it so?
I have used "gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)"
You can't define two functions with the same name, which differ only in return type. So define function with different name, for example:
std::vector<int> getValueCopy() const;
By adding const to the first function you render calls to getValue ambiguous: What is the difference between those 2 functions:
std::vector<int> getValue() const; // 1
const std::vector<int>& getValue() const; // 2
Well, they are the same, except for the return value, but wait! You can't overload based on the return type in C++! It wouldn't make sense, most calls would be ambiguous:
std::vector<int> y = m.getValue(); // which one? It can be 1, it can be 2 (std::vector<int>
// is not a better match than const std::vector<int>&)
const std::vector<int>& z = m.getValue(); // same as above
m.getValue(); // which one?
But also, what is supposed to be the difference between the two?
The first one is 100% safe, while the second one is not: one could store a reference to x, and it becomes a dangling reference if the object gets destroyed. And so I would say that you get rid of the second one, if possible.
your problem is that you didn't understand functions overloading concept
when you overload function The definition of the function must differ from each other by the types of argument or the number of arguments in the argument list.
You can not overload function declarations that differ only by return type.
in your functions :
std::vector<int> getValue() const
const std::vector<int>& getValue() const
it only differ in return type so it will be not considered as overloaded functions
best way to correct your error is to change your second function name to getValuev2()
or change arguments of one of the functions.
you can read more about overloading in C++ :
https://www.tutorialspoint.com/cplusplus/cpp_overloading.htm

When there is no non-const operator overload, will non-const object use const operator?

It seems it is in VS2013. But why in effective c++' item 3, the non-const operator calls const operator when they do exactly the same thing?
This is the code in Effective c++ item 3:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const // same as before
{
...
...
...
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return const_cast<char&>( // cast away const on type;
static_cast<const TextBlock&>(*this)[position] // add const to *this's type;call const version of op[]
);
}
...
};
A const object cannot use a non-const method. (Remember that operators are only methods).
But the converse is true: A non-const object can use a const method (although it will use a non-const equivalent if that's present).
A method is only const if it is marked as const: a compiler is not allowed to assert const-ness by inspecting the function body.
Yes: if a const member function is present, but no non-const version, then calling that function on a non-const object will use the const function.
#include <iostream>
struct Foo {
void bar() const { std::cout << "const bar()\n"; }
};
int main() {
Foo f;
f.bar();
}
prints const bar().
Note the reverse is not true. You may not call a non-const member function on a const object:
struct Foo {
void bar() { std::cout << "non-const bar()\n"; }
};
int main() {
const Foo f;
f.bar();
}
gives a compiler error:
SO.cpp: In function 'int main()':
SO.cpp:9:11: error: passing 'const Foo' as 'this' argument of 'void Foo::bar()' discards qualifiers [-fpermissive]
f.bar();
^
EC++ Item 3, Use const whenever possible has a subsection, Avoiding Duplication in const and Non-const Member Functions. The reason it suggests that you don't just let the compiler pick the const version is that the return types are different (by a const) - the non-const version casts this away:
class CharWrapper {
char c_;
public:
char const& getC() const
{
std::cout << "const getC()\n";
return c_; // pretend this line is complicated
}
char& getC()
{
std::cout << "non-const getC()\n";
char const& c = const_cast<CharWrapper const&>(*this).getC(); // call the right one to avoid duplicating the work
return const_cast<char&>(c); // fix the result type
}
};
int main() {
CharWrapper wrapper;
wrapper.getC() = 'a';
}
Notice that it calls const operator and casts away constness (it one of those few times where it does not result in UB). It is needed so non-const operator returns a mutable reference. Otherwise you would not be able to modify objects contained in vector at all.
Call to existing operator instead of writing code again is done to avoid code duplication and possible future errors when you change one function, but forgot the other.
Regarding your comment: there is no move involved. It returns a reference. I would expect no difference in generated code for both const and non-const operator.

Copying member variable through const reference to a class

My understanding of const references to a class was that we cannot modify the state of the class i.e. cannot perform any action that modifies any of it's member variables. But consider the following code.
#include <iostream>
#include <vector>
struct Vec {
Vec(std::vector<int>& v) : vec(v) {}
Vec(const Vec& v) : vec(v.vec) {
v.vec.resize(100);
}
std::vector<int>& vec;
};
int main() {
std::vector<int> x;
Vec v1(x);
Vec v2(v1);
v1.vec.resize(10, 0);
v2.vec[5] = 8;
std::cout << v1.vec[5] << std::endl;
return 0;
}
I compiled this code using g++:4.8.3 with -Wall flag and it compiled. I have two questions.
In Vec copy construtor, we pass a const reference to a Vec v. By extension of constness of v, v.vec should also have been a const std::vector<int>&. Then how does it get copied to type std::vector<int> ?
The only logical way the above can happen is if constness of a class doesn't apply to it's member variable. So my question is what implications does a constness of a class has on it's member variables ?
The constness of a class applies to its member variables, but not to the referents of the member variables that are references. This is similar to pointer members, where the pointer would be const, but not what it points to.
In Vec copy construtor, we pass a const reference to a Vec v. By extension of constness of v, v.vec should also have been a const std::vector&. Then how does it get copied to type std::vector ?
The only logical way the above can happen is if constness of a class doesn't apply to it's member variable. So my question is what implications does a constness of a class has on it's member variables ?
That's not really how const extends. You stick the const on the back of the type, not the front. Consider the following code...
struct foo {
foo() {}
int * i;
};
int main (void)
{
foo my_foo;
int * &a = my_foo.i;
const int * &b = my_foo.i;
int * const &c = my_foo.i;
const foo const_foo;
int * &d = const_foo.i;
const int * &e = const_foo.i;
int * const &f = const_foo.i;
return 0;
}
foo.cpp: In function ‘int main()’:
foo.cpp:12: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int*’
foo.cpp:16: error: invalid initialization of reference of type ‘int*&’ from expression of type ‘int* const’
foo.cpp:17: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int* const’
This shows that const_foo.i has type int * const, which is different from const int *. The type int * const makes no promises about its pointed at data not changing, just the pointer itself can't change.
In your example, v2.vec would have type std::vector<int> & const. But that type is meaningless (and illegal) because you can't change the alias of a reference anyhow. std::vector<int> is already const for this purpose.
It is possible to have const-ness inherit, but you have to explicitly code that rule. The following code will happily refuse to compile due to enforcing const restrictions by restricting callers to use a getter that makes the contract you are looking for...
#include <iostream>
#include <vector>
struct Vec {
Vec(std::vector<int>& v) : _vec(v) {}
Vec(const Vec& v) : _vec(v.vec()) {
v.vec().resize(100);
}
// How to make const-ness inherit...
std::vector<int> & vec() { return _vec; }
std::vector<int> const & vec() const { return _vec; }
private:
std::vector<int>& _vec;
};
int main() {
std::vector<int> x;
Vec v1(x);
Vec v2(v1);
v1.vec().resize(10, 0);
v2.vec()[5] = 8;
std::cout << v1.vec()[5] << std::endl;
return 0;
}
Once you start doing that you get into strange territory however, as it allows me to call std::vector<int> const & vec() const, save a 'data-const' reference to _vec, and then have other code change the data in vec, violating the const contract to the earlier code. There are a number of landmines out there, which may be why the language doesn't have this sort of const inheritance built-in.

Returning iterator from constant member function

In the following code why the return type of foo::func is vector<int>::const_iterator and not vector<int>::iterator though I am returning an object of vector<int>::iterator.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class foo
{
private:
vector<int> ar;
public:
foo()
{
ar.resize(10);
iota(ar.begin(), ar.end(), 1);
}
auto func() const
{
return ar.begin() + 5;
}
};
int main()
{
foo x;
cout<<boolalpha<<endl;
auto it = x.func();
cout<<is_same<decltype(it), vector<int>::iterator>::value<<endl;
cout<<is_same<decltype(it), vector<int>::const_iterator>::value<<endl;
return 0;
}
Output of above code is :
false
true
Instead if I redefine foo::func() as
auto func()
{
return ar.begin() + 5;
}
The output will be
true
false
Why the constant member function is changing return type to constant?
And should I need to remove const keyword to make the return type as vector<int>::iterator or is there any other way?
A member function declared const affects the type of the this pointer. Within func(), this has type const foo*. As a result, all member types accessed via a const this pointer will themselves be const, so the implicit type of ar is in fact const std::vector<int>. Of the two begin() overloads for vector, the only viable overload is the const overload, which returns a const_iterator.
When you redefine func() to be non-const, then the type of this is simply foo*, so the type of the member ar is std::vector<int> and the overload of begin() returning iterator is preferred.
There are two std::vector::begin depending on whether the instance is const or not.
In foo::func() const, you are trying to access ar which is then viewed as a const std::vector<int>, and std::vector::begin() const returns a const_iterator.
When you access ar in foo::func(), it is then viewed as a std::vector<int>, without const, and begin will then refer to std::vector::begin() (without const again), which is not the same function.
Similarly, your own foo class could define two versions of foo::func simultaneously:
auto func()
{ return ar.begin() + 5; }
auto func() const
{ return ar.begin() + 5; }
And the constness of the instances will decide which version to call:
foo x;
const foo y;
x.func(); // First version, get an iterator
y.func(); // Second version, get a const_iterator
The iterator points to one of its data members and so if the function is const then the iterator must be const too otherwise you could change the data contained in the class from the iterator returned by a const member function.
That's my understanding at least - I would be grateful if someone else could confirm or deny this.
Member function begin is overloaded the following way
iterator begin() noexcept;
const_iterator begin() const noexcept;
Thus functions that are declared with qualifier const deal with constant objects. That means that data members of a constant object if they are not declared as mutable are also constant. In this case the second overloaded function begin is called for data member ar that returns const_iterator.

Invalid initialization of reference : difference between pointer and value?

Considering the following code :
#include <iostream>
#include <vector>
template<typename Type> class MyClass
{
public:
MyClass(Type* ptr) : _ptr{ptr}, _val{*ptr} {;}
inline Type*& getptr() {return _ptr;}
inline Type*& getptrc() const {return _ptr;}
inline Type& getval() {return _val;}
inline Type& getvalc() const {return _val;}
protected:
Type* _ptr;
Type _val;
};
int main()
{
std::vector<double> v = {0, 1, 2};
MyClass<const double> x(&v[0]);
x.getval();
x.getvalc(); // <- OK
x.getptr();
x.getptrc(); // <- ERROR : "invalid initialization of reference of type 'const double*&' from expression of type 'const double* const'"
return 0;
}
GCC produce an error for the getptrc function invalid initialization of reference of type 'const double*&' from expression of type 'const double* const'. But the function getvalc compiles well. I do not understand the difference between getvalc and getptrc that is at the origin of the error.
What is the cause of the error and why I can't put a const for a function that returns a reference to a pointer ?
const double*& is a reference to a pointer to a const double.
const double* const is a const pointer to a const double.
This means that you have to return a constant pointer.
inline Type* const & getptrc() const {return _ptr;}
const on methods means that you will not modify the data member. To fullfill that contract, you have to return a constant pointer because otherwise, you could modify the data member _ptr. However, in your other case with getvalc, you already have fullfilled that contract by returning a const double.
In MyClass<const double>, the name Type refers to const double. So the member function getvalc(), which returns a Type&, is fine: it returns a const double&, and _val cannot be modified through that reference.
There's another layer of indirection for Type*&; although Type is const double, the pointer that points to it is modifiable; that's why the compiler is complaining: _ptr is const inside the const member function, but the function attempts to return it as a modifiable pointer.