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
Related
I have a class B that has two methods, where one returns a pointer to a member variable and the other returns a const reference to the variable.
I try to call those methods. During the calls, I store the return values to respective return types.
I was expecting the appropriate return types would end up calling the appropriate methods, but I get a compilation error saying:
error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]
const int& refval2 = b.Get(); `
Here is my code:
#include <iostream>
class B{
public:
int* Get(){
return &x_;
}
const int & Get() const{
return x_;
}
private:
int x_ = 0;
};
int main(){
B b;
const int& refval2 = b.Get();
int* pval2 = b.Get();
}
Return type is not part of function signature, it's not considered in overload resolution.
In general, the candidate function whose parameters match the arguments most closely is the one that is called.
For non-static member function call, the type of the object to be called on involves too. There're two Get(), one is const and one is non-const. For b.Get();, the non-const Get() is an exact match; for the const Get() to be called the object b has to be converted to const. Then the non-const one wins, after that the compiler will try to convert the returned int* to const int& and fails.
So
B b;
int* pval2 = b.Get(); // call to non-const Get()
const B cb;
const int& refval2 = cb.Get(); // call to const Get()
Why is the int* Get() called insted of const int& Get()?
const int & Get() const
Is a const member function.
From class.this:
If the member function is declared const, the type of this is const X*
However, you declared:
B b;
as non-const which will call the non-const function int* Get(). Thus, the error.
There are two things
return type are not considered in overload resolution
Const ness of method is considered in over load resolution
If you remove const in second method, compiler will complain about ambiguity.
As already answered return type is not considered. So I see 3 possible solutions:
1 make it part of function signature:
class B {
public:
void Get( int *&refptr );
void Get( int &refval ) const;
};
int main(){
B b;
int refval2 = 0;
b.Get( refval2 );
int* pval2 = nullptr;
b.Get( pval2 );
}
but this produces pretty ugly code, so you better use method 2 or 3 -
2 use different function names, this is pretty obvious
3 use ignored parameter:
class B {
public:
int *Get( nullptr_t );
const int &Get() const;
};
int main(){
B b;
const int &refval2 = b.Get();
int* pval2 = b.Get( nullptr );
}
The problem is that the compiler thinks the two Get() functions are the same. If you create two identical functions with only varying return types, the compiler has no idea which function you want to use. To fix the problem, change the name of one or both of the Get() functions; say GetXPtr() and GetX().
Update, thanks to AlexD's answer: This question boils down to the language rules for selecting member function overloads, which is discussed in the following question: Calling a const function rather than its non-const version
When the range expression of a range-based for loop is a call to a member function with const and non-const overloads, it seems that the non-const overload is selected. As a result, the following program does not compile:
#include <iostream>
#include <vector>
class foo {
public:
const std::vector<int>& get_numbers() const { return numbers; }
protected:
std::vector<int>& get_numbers() { return numbers; }
private:
std::vector<int> numbers;
};
int main() {
foo f;
for (int x : f.get_numbers()) std::cout << x << std::endl;
}
Diagnostic message from gcc 5.3:
error: ‘std::vector<int>& foo::get_numbers()’ is protected
But a const version of get_numbers() is available and could be used. We can force it to be used by using a const reference to the foo instance, like this:
int main() {
foo f;
const foo& g = f;
for (int x : g.get_numbers()) std::cout << x << std::endl;
}
Is there a better/easier way to tell the compiler that it can and should use the const member function overload, without explicitly making a const reference to the object?
There are some similar questions about making the range-based for loop use const iterators, but I haven't found any questions about making the loop's range expression const for the purposes of selecting function overloads.
But a const version of get_numbers() is available and could be used.
The best function is selected before accessibility is considered. The standard states (emphasis mine):
If a best viable function exists and is unique, overload resolution succeeds and produces it as the result.
Otherwise overload resolution fails and the invocation is ill-formed. When overload resolution succeeds, and
the best viable function is not accessible (Clause 11) in the context in which it is used, the program is
ill-formed.
A simple template function as_const can make the cast not so ugly. I believe this is being added or was recently added to the standard library.
template <typename T> T const & as_const (T const & t) { return t; }
void f ()
{
for (auto && x: as_const (y)) {}
}
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.
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.
I am trying to understand the trailing return based new function declaration syntax in C++11, using decltype.
In the following code, I am trying to define a member function returning a const & to allow for read-only access to i
#include <iostream>
#include <type_traits>
struct X {
int &i;
X(int &ii) : i(ii) {}
// auto acc() const -> std::add_const<decltype((i))>::type { return i; } // fails the constness test
auto acc() const -> decltype(i) { return i; } // fails the constness test
// const int &acc() const { return i; } // works as expected
};
void modify_const(const X &v) {
v.acc() = 1;
}
int main() {
int i = 0;
X x(i);
modify_const(x);
std::cout << i << std::endl;
return 0;
}
As mentioned in the comments, only the last commented version of acc() works, whereas using the others, the code just compiles and prints value 1.
Question: How do we have to define the acc() function using the new function declaration syntax based on decltype, such that the compilation here fails due to returning a const &int in modify_const, or in other words, such that acc() has a proper const &int return type.
Remark: using int i; instead of int &i; as the member variable in X produces a compile error, as expected.
Edited to better distinguish between constness of v and X::i, respectively. It is the latter I am trying to impose in acc().
The problem is that decltype((i)) return int& and apply const to that type has no effect. You want something like
template <typename T> struct add_ref_const { typedef T const type; };
template <typename T> struct add_ref_const<T&> { typedef T const& type; };
... and then use
auto acc() const -> typename add_ref_const<decltype((i))>::type { return i; }
That is, the const needs to go between the type T and the &. The solution would have been obvious if you had put the const into the correct location: const should go to the right.
There's nothing illegal about a const member function modifying the target of a pointer to non-const, even if that pointer was gotten from a member variable.
From the compiler's perspective, int& IS the correct return type.
Your "modify_const" function is incorrectly named. i is what gets modified, and is not const.
Simply add an & to the left and skip the trailing return type.
struct X {
int &i;
X(int &ii) : i(ii) {}
auto& acc() const { return i; } // Returns const reference
auto& acc() { return i; } // Returns non-const reference
const auto& acc() const { return i; } // Add const to the left to make it even more readable
};
Note that using this syntax you can declare the member variable after you have declared the function.