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.
Related
I know that if we add a "const" qualifier after a function declaration, it implies the implicit "this" pointer become const so we cannot directly modify any member
However, in a const member function, does every member automatically become const as well?
To illustrate what I'm trying to ask, here is a demo program
#include <vector>
struct Foo {
int a;
int b;
};
class Demo {
public:
Demo() = default;
void bar(std::vector<Foo>::iterator it) const {}
void baz() const {
// begin() is supposed to return iterator, not const_iterator
bar(vector.begin());
}
private:
std::vector<Foo> vector;
};
int main(void)
{
Demo d;
d.baz();
return 0;
}
Inside baz() method, I'm calling std::vector::begin() method, which according to doc, can either return a iterator or const_iterator
iterator begin() noexcept;
const_iterator begin() const noexcept;
My understanding is that, if member "vector" is declared as const, e.g.
private:
const std::vector<Foo> vector;
Then the overloaded form that returns a const_iterator shall be invoked. Otherwise, the overloaded form that returns a mutable iterator shall be invoked, which is the current case
When I try to compile it
g++ --std=c++17 -Wall
I got a compile error
main.cpp:17:13: error: no viable conversion from '__wrap_iter<std::__1::vector<Foo, std::__1::allocator<Foo> >::const_pointer>' to
'__wrap_iter<std::__1::vector<Foo, std::__1::allocator<Foo> >::pointer>'
bar(vector.begin());
^~~~~~~~~~~~~~
which essentially vector.begin() returns a const_iterator rather than iterator.
Now I'm confused, I do not declare vector as const, why the overloaded form to return const_iterator is still invoked?
Is it because baz() is declared as const? I understand that in a const member function, all memory directly associated with current object shall not be modified, but I don't expect this to cause such impact on function overloading. I expect vector.begin() to return a const_iterator if and only if vector is declared as a const member, e.g.
private:
const std::vector<Foo> vector;
Thanks in advance
Like everybody has said, if this is const then the members of whatever this points to are const.
To add on, you can have a member variable be non-const in a const function or context if you declare it with the mutable keyword... if you want that kind of behavior.
class Demo {
public:
Demo() = default;
void bar(std::vector<Foo>::iterator it) const {}
void baz() const
{
// begin() now returns iterator instead of const_iterator
bar(vector.begin());
}
private:
mutable std::vector<Foo> vector; // vector is non-const in const functions
};
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.
I have this code:
template <class T>
class Something
{
T val;
public:
inline Something() : val() {}
inline Something(T v) : val(v) {}
inline T& get() const { return val; }
inline Something& operator =(const Something& a) { val = a.val; return *this; }
};
typedef Something<int> IntSomething;
typedef Something<const int> ConstIntSomething;
class Other
{
public:
IntSomething some_function()
{
return IntSomething(42);
}
ConstIntSomething some_function() const
{
return ConstIntSomething(42);
}
};
void wtf_func()
{
Other o;
ConstIntSomething s;
s = o.some_function();
}
However, the compiler picks the wrong overload of Other::some_function() in wtf_func() (i.e. the non-const one). How can I fix this? Note that for certain reasons I cannot change the name of Other::some_function().
o is not const-qualified, so the non-const some_function is selected. If you want to select the const-qualified overload, you need to add the const qualifier to o:
Other o;
Other const& oref(o);
ConstIntSomething s;
s = oref.some_function();
When overload resolution occurs, the compiler only looks at the o.some_function() subexpression; it does not look at the context around the function call to decide to pick something else. Further, the return type of a member function is not considered during overload resolution.
Note that it may make more sense for IntSomething to be implicitly convertible to ConstIntSomething, either using an operator ConstIntSomething() overload in IntSomething (less good) or using a non-explicit ConstIntSomething(IntSomething const&) constructor in ConstIntSomething (more good).
It doesn't pick the wrong overload; const-ness is resolved by whether this is const or not. In your case, o is non-const, so the non-const overload is picked.
You can hack this by creating a const-reference to o, e.g.:
const Other &o2 = o;
s = o2.some_function();
But really, you should probably be considering your overloads in Something. For instance, you can't currently do this:
IntSomething x;
ConstIntSomething y;
y = x;
which doesn't sound correct. Why shouldn't you be allowed to take a const ref to a non-const ref?
Your object o needs to be const object for a const function to be called on it. Otherwise the compiler rightly picks up the non const version of the function.
The compiler picks the overload to use based on the constness of the object that will become this. You can make it call the desired version with static_cast: s = static_cast<const Other&>(o.some_function());
You might also want to copy the new behaviour found in the containers of the C++0x standard library. Containers such as vector now have members cbegin() and cend() that return a const_iterator whether the container is const or not unlike begin() and end()
class Other {
// Rest of other
public:
// No overload for non-const
// Even if called with a non const Other, since this member is marked
// const, this will be of type Other const * in all cases and will call
// the const qualified overload of some_function.
ConstIntSomething csome_function() const
{
return some_function();
}
};
if this is a const pointer to class's object how can you return a const pointer from non-const return type?
Class T
{
public:
T* func(){return this;}
};
Firstly, this is not a "const pointer". Where did you get that strange idea? Pointer this has scalar type and is not an lvalue, meaning that it can't possibly be "const" or "non-const". Pointer this is an rvalue and it is non-modifiable.
Secondly, the code in your question is valid regardless of whether the pointer involved is const or not. For example, the following code is valid for exactly the same reason
int *const p = 0; /* a const pointer */
int *foo() {
return p; /* OK, no error here */
}
What is returned in this case is a completely independent copy of the original pointer value. It does not matter whether p is const or not - making a copy of const value does not in any way violate its constness.
This is exactly what's done in your code sample - you are returning a copy of this's value. Why would you even care whether this is const or not?
Unless the member function is const-qualified (e.g. T* func() const instead of just T* func()), this is of type T*.
this is not itself modifiable (so you can't assign something to this), but it's just a pointer to an object of type T like any other and can be used as such.
AndreyT provided the explanation and here is the reference.
From standard docs 9.3.2.1 The this pointer,
In the body of a non-static (9.3) member function, the keyword this is an rvalue expression whose value is the address
of the object for which the function is called. The type of this in a member function of a class X is X*. If the member
function is declared const, the type of this is const X*, if the member function is declared volatile, the type of
this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile
X*.
which explains the question that you asked and the comment you made to AndreyT.
Hope this one also helps.
A pointer is not a object, so it does not matter if the pointer is const when it is a return value.
Consider this:
int x;
int f() {
return x;
}
const int g() {
return x;
}
There's actually no difference between f() and g().
But it may make difference if you replace all "int" to some other name of a class.
"const" may prevent you from some error, e.g. when you don't write a copying constructor properly.
class T
{
int *x;
public:
T() {x = new int;}
void set_x(int n) {*x = n;}
int get_x() const {return *x;}
};
T a;
T f() {
return a;
}
const T g() {
return a;
}
int main() {
f().set_x(123); // The value is modified
// even though a return value is not a l-value.
g().set_x(123); // Compile Error
}
So, if you want to prevent modifying data of the object by referring to the return value, what you need is:
class T
{
public:
const T* func() const
{ return this; }
};
Please consider the following code.
struct foo
{
};
template<typename T>
class test
{
public:
test() {}
const T& value() const
{
return f;
}
private:
T f;
};
int main()
{
const test<foo*> t;
foo* f = t.value();
return 0;
}
t is a const variable and value() is a constant member-function which returns const T&. AFAIK, a const type is not assignable to a non-const type. But how foo* f = t.value(); compiles well. How this is happening and how can I ensure value() can be only assigned to const foo*?
Edit
I found that, this is happening on when templates are used. Following code works as expected.
class test
{
public:
test() {}
const foo* value() const { return f; }
private:
foo* f;
};
int main()
{
const test t;
foo* f = t.value(); // error here
return 0;
}
Why the problem is happening when templates are used?
Because you have two levels of indirection - in your main function, that call to value returns a reference to a const pointer to a non-const foo.
This can safely be copied into non-const pointer to a non-const foo.
If you'd instantiated test with const foo *, it would be a different story.
const test<const foo*> t;
foo* f = t.value(); // error
const foo* f = t.value(); // fine
return 0;
Update
From the comment:
value() returns const T& which can
only be assigned to another const
type. But in this case, compiler is
safely allowing the conversion.
Const data can only be read. It cannot be written ("mutated"). But copying some data is a way of reading it, so it's okay. For example:
const int c = 5;
int n = c;
Here, I had some const data in c, and I copied the data into a non-const variable n. That's fine, it's just reading the data. The value in c has not been modified.
Now, suppose your foo had some data in it:
struct foo { int n; };
If I have a non-const pointer to one of those, I can modify the n value through the pointer. You asked your test template to store a pointer to a non-const foo, and then made a const instance of test. Only the pointer address is constant, therefore. No one can change the address stored in the pointer inside test, so it cannot be made to point to another object. However, the object it points to can have its contents modified.
Update 2:
When you made your non-template version of the example, you made a mistake. To get it right, you need to substitute foo * into each place where there's a T.
const T& value() const
Notice that you have a reference to a const T there. So the return value will be a reference to something const: a foo *. It's only the pointer address that can't be modified. The object it points to can have its contents modified.
In your second example, you got rid of the reference part, which changes the meaning and makes the const modifier apply to the object that the pointer points to, instead of applying to the pointer itself.
Use the following template specialization:
template<typename T>
class test<T*>
{
public:
test() {}
const T* value() const
{
return f;
}
private:
T* f;
};
After including this, g++ says:
d.cpp: In function ‘int main()’:
d.cpp:41: error: invalid conversion from ‘const foo*’ to ‘foo*’
There's nothing wrong in your code, having a const reference to a pointer only means that you can't modify the pointer, but the pointed-to object remains perfectly mutable. If inside your main function you try to change the address pointed to by the f member of t you'll see that you can't: encapsulation is perfectly preserved.
This is the same principle that makes the following code valid:
void foo(std::vector<int *> const & v)
{
*v[0] = 0; // op. [] returns const & to int *
}
People new to C++ are usually surprised by this behavior, because for them a const vector should not allow the modification of its elements. And in fact it doesn't, because the pointer stored in the vector does not change (it keeps pointing to the same address). It's the pointed-to object which is modified, but the vector does not care about that.
The only solution is to do as Amit says and provide a specialization of your class for T*.