this is my generic class:
template<class T, class PrnT>
class PersonalVec {
public:
PersonalVec();
T &operator[](int index) const;
const T &operator[](int index) const;
private:
std::vector<T> _vec;
};
I'm required to implement 2 versions of [] operator:
one that will return a const reference and a regular one that will also return a reference.
When i compile it i get:
PersonalVec.hpp:23: error: ‘const T& PersonalVec<T, PrnT>::operator[](int) const’ cannot be overloaded
PersonalVec.hpp:22: error: with ‘T& PersonalVec<T, PrnT>::operator[](int) const
I've put either one of them as remark and then it does compile, so i guess they are colliding somehow. What is the problem and how can i fix it?
thank you!
You need:
T &operator[](int index);
const T &operator[](int index) const;
i.e. non-const operator returns non const reference and const one returns the const reference.
You can't overload based on return type, you can only overload based on parameter types, including the hidden this parameter for member functions.
The type of a function call expression, or an expression involving a potentially overloaded operator, is determined by the function type chosen by overload resolution, you cannot force such an expression to have a particular type and try to influence the overload resolution from the return type.
You need to either give your overloaded functions signatures that differ by parameter types or the constness of this, or you need to pick one appropriate return type and have a single function.
You will need to remove constness of the function when returning the non-const reference.
T &operator[](int index);
const T &operator[](int index) const;
Overloading does not and can not happen on the return type.
Return type of a function is not a criteria that can be used for overloading of functions.
A function can be overloaded if:
1. Different no of arguments
2. Differnt Sequence of arguments or
3. Different types of arguments
You are trying to overload the function based on return type and hence it gives the error.
The 'const' keyword can help you overload functions even if the above 3 criterias are not met. So simple solution can be to make one of the function const and keep other as a normal function
Related
Can I have two overloaded operator[] like this in the same class?
I am confused which definition is used when I use operator[], isn't int ambiguous? Don't they have the same signature?
template <class T, int n>
class ArrayTP
{
private:
T ar[n];
public:
ArrayTP() {};
virtual T & operator[](int i);
virtual T operator[](int i) const;
};
This class contains declarations of these overloaded operator. I have not included definition in my question, though.
Overloaded operators works no different than normal overloaded functions. It's just they are special function. So I'm giving you general example from function same applies to any kind of functions.
As you must know that, top-level const has no effect on the
objects that can be passed to the function. A parameter that has a top-level const is
indistinguishable from one without a top-level const:
Record lookup(Phone);
Record lookup(const Phone); // redeclares Record lookup(Phone)
Record lookup(Phone*);
Record lookup(Phone* const); // redeclares Record lookup(Phone*)
In these declarations, the second declaration declares the same function as the first.
On the other hand, we can overload based on whether the parameter is a reference
(or pointer) to the const or nonconst version of a given type; such consts are
low-level.
// functions taking const and nonconst references or pointers have different parameters
// declarations for four independent, overloaded functions
Record lookup(Account&); // function that takes a reference to Account
Record lookup(const Account&); // new function that takes a constbreference.
Record lookup(Account*); // new function, takes a pointer to Account
Record lookup(const Account*); // new function, takes a pointer to const
In these cases, the compiler can use the constness of the argument to distinguish
which function to call. Because there is no conversion from const,
we can pass a const object (or a pointer to const) only to the version with a
const parameter. Because there is a conversion to const, we can call either
function on a nonconst object or a pointer to nonconst. However, the compiler will prefer the nonconst versions when we pass a
nonconst object or pointer to nonconst.
Examples from primer.
I have 3 attempts to overload function "begin()"
class Test
{
public:
//case 1: compiler error
int begin();
const int begin();
//case 2:compiler warning: type qualifiers ignored on function return type
int begin();
const int begin() const;
//case 3: ok
int begin();
int begin() const;
};
For the two cases that build successfully, how does the compiler choose which overload is called for begin()?
Member functions have an implicit argument which is the instance of the class itself. So you can think your functions as really looking like:
// 1) compile-error as const-qualifications on return doesn't distinguish
// the functions - you cannot overload on return type
int begin(Test& );
const int begin(Test& );
// 2)
int begin(Test& );
const int begin(const Test& );
// 3)
int begin(Test& );
int begin(const Test& );
With the 2nd and 3rd cases, the const-qualification on the function is equivalent to that implicit argument being a reference-to-const. So when you have something like:
Test{}.begin();
That calls begin() using a reference to non-const Test as the implicit first argument. Both overloads of begin() are viable, both require no conversions, so the least cv-qualified reference is preferred, which is the non-const-qualified function.
Instead, when you have:
(const Test{}).begin();
we're calling begin() with a reference to const Test. So the non-const-qualified function is not a viable candidate (you cannot pass a const-ref to a function expecting a non-const-ref), so the best viable candidate is the only viable candidate: int begin() const.
Case 1 is ambiguous cause return type doesn't participate in overload resolution,
case 2 is equivalent to case 3 because const is discarded in return type
In case 3:
const Test t1;
t1.begin() // calls int begin() const;
Test t2;
t2.begin() // calls int begin();
From cppreference.com:
A non-static member function can be declared with a const, volatile,
or const volatile qualifier (this qualifier appears after the name of
the function in function declaration). Differently cv-qualified
functions have different types and so may overload each other. In the
body of a cv-qualified function, the this pointer is cv-qualified,
e.g. in a const member function, only other const member functions may
be called normally. (A non-const member function may still be called
if const_cast is applied or through an access path that does not
involve this.)
A few rules to remember:
You can't overload methods based just on the return type. This leads to the error in your Case 1.
When returning by value, the const qualifier is redundant. In your case 2, compiler is warning you about this.
Every non-static method of the class has an implicit argument. On method invocation, this is assigned to the instance on which the method is invoked. The const keyword at end of method declaration(before the method body) applies to this implicit argument. What you are essentially saying is that the body of the function will not modify the state(data members) of the instance. If you call the method on a const instance, then you need to define the const method. Also, you can overload the method solely based upon the fact whether it is const or not.
I got an assignment to implement a template array class.
One of the requirement is to overload the [] operator.
I made this two const and non-const version which seems to be working fine.
const T& operator[](const unsigned int index)const
and
T& operator[](const unsigned int index)
My question is how will the compiler know which one to run
when i will do something like:
int i=arr[1]
On a non-const array?
The non-const function will always be called on a non-const array, and the const function on a const array.
When you have two methods with the same name, the compiler selects the best-fitting one based on the type of the arguments, and the type of the implicit object parameter (arr).
I just answered a similar question the other day, you may find it helpful: https://stackoverflow.com/a/16922652/2387403
It all depends on your declaration of the object. If you have
const T arr[];
...
int i=arr[1];
Then the const version will be called, but if you have
T arr[];
...
int i=arr[1];
Then the non-const version will be called. So in the example you gave, since it was a non-const array, the non-const version will be called.
I overload an operator twice with the same parameter list. but with different return type:
T& operator()(par_list){blablabla}
const T& operator()(par_list){blablabla}
So when I'm calling the () operator, which function would be called based on what preference or situation? I know that if I call () under const function it has to be the const T& one.
I'm just curious how C++ deal with such situation and how the default preference works.
Thanks
These functions don't overload each other; they have the same signatures, and so the attempt to redefine the same function, which is an error. The return type is not part of a function's signature. To overload a function, you must declare a second function with the same name, but different parameters or const/volatile qualifiers - that is, qualifiers on the function, not the return type.
(They don't override each other either; overriding is what derived classes do to their base classes' virtual functions).
It's common to define a const and a non-const overload of a member function; the const overload must declare the function const, not just the return type:
T& operator()(par_list){blablabla}
const T& operator()(par_list) const {blablabla}
^^^^^
Now the first will be called if you apply () to a non-const object, and the second on a const object. For example:
Thingy nc;
Thingy const c;
nc(); // calls the first (non-const) overload
c(); // calls the second (const) overload
You can't overload a function/method based on return type. I would expect the compiler to throw an error here. What you can do is specify the method itself as a const method, using
const T& operator()(par_list) const {blahblah}
The const qualifier not only means this can be called on a const receiver, but it also is used in the overload resolution. This happens because it affects the implicit *this parameter that's passed to the method; a const method uses a const qualifier on *this, and const qualifiers are taken into account during overload resolution.
The way you define your operators, no way the compiler can decide which operator() to call. Overloading of functions (and operators) can only be done on the type of the arguments, never on the return type. And in fact, you will have an error at compilation as soon as you define the second one, the compiler considering that you are redefining the same function/operator.
However, the following is common (and probably what you have):
T& operator()(par_list){blablabla}
const T& operator()(par_list) const {blablabla}
This additional "const" after the argument list exists because you are defining non-static member functions and member functions have an implicit hidden argument: the "this" pointer to the instance of the class. The "const" keyword there indicates if this hidden pointer is to a const instance or not. This argument participates to the overloading resolution and that is in this case what the compiler use to choose which version of the operator to use.
So:
class A {
T& operator()() { ... }
const T& operator()() const { .... }
};
A a;
const A& ca(a);
a(); -> returns a T&
ca(); -> returns a const T&
I have a struct with these operators (given some type T):
T& operator[](size_t i) { return write(i); }
const T& operator[](size_t i) const { return get(i); }
When I access a non-const object of this by [i], will it always use the non-const operator[] or will it automatically see which one is needed (e.g. if the const-version is "enough") and take that one? What are the rules there? And do most compilers (I mostly care about Clang, recent GCC and recent MSVC) behave the same there?
Background: write(i) could be much more costly than get(i). I even have some code where the behavior is slightly different, where write(i) might touch some files, set some modified-flags or whatever.
As with any member function, the choice of overload depends on the type of the implicit instance argument:
T x;
x.foo(); // #1
const_cast<T const &>(x).foo(); // #2
In case #1, the implicit instance argument has type T, so the overload T::foo() is viable and will be selected. In case #2, the implicit instance argument has type T const, and thus only an overload T::foo() const is viable, and will be selected if it exists.
The normal overload resolution rules are at play here: If you have both const and non-const overloads and the instance argument is non-constant, then the non-constant version is chosen because it requires zero conversions, while the const overload would require one (standard) conversion, namely from T & to T const &, and thus that overload is not as good a fit as the non-const one.
It will always use the non-const overload if you have a non-const object.
If you want to control it manually, use const_cast ... although, if the methods have different behaviour and you want to make it clear what you're doing, it's probably better to call a named method instead.