I've written the following code and run into a problem with overloading operator [].
Here's the code for testmain.cpp:
#include"test.hpp"
int main()
{
C tab = C(15);
bool b = tab[2];
return 0;
}
And here's the header file test.hpp:
#ifndef _test_
#define _test_
class C
{
friend class ref;
int s;
public:
class ref
{
public:
bool v;
ref();
ref(bool x);
ref& operator = (ref a);
ref& operator = (bool x);
};
C();
C(int a);
bool operator[] (int i) const ;
ref operator[] (int i);
};
#endif ///_test_
When I attempt to compile the code, I get the following error:
testmain.cpp: In function ‘int main()’:
testmain.cpp:6:16: error: cannot convert ‘C::ref’ to ‘bool’ in initialization
Looks like the compiler automatically assumes that my indexing operator[] will always return an object of type ref and ignores the operator[] that returns boolean type variable.
Is it possible to fix the code so that the compiler "knows" when to use appropriate overloaded operator[]?
The overload returning bool is const, so will only be used when applied to a constant C object. Yours is not const, so the non-const overload is chosen.
One solution is to make ref implicitly convertible to bool:
operator bool() const {return v;}
so that you can read a bool value in the same way using either overload.
You have two implementations of operator[]...one for const objects and one for non-const objects. Your main has a non-const instance of C, so it's invoking the non-const operator, which returns a ref.
Related
I have this little piece of code:
template<typename T>
class Test
{
public:
//operator T() const { return toto; }
T toto{ nullptr };
};
void function(int* a) {}
int main(int argc, char** argv)
{
Test<int*> a;
function(a);
return 0;
}
It doesn't compile unless the line operator T() const { return toto; } is un-commented. This magically works, but I am not sure why (if I un-comment the line).
I do understand that if the line is commented, the type of a when passed to function() is incompatible with the expected type int*. So, of course, the compiler complains ... no problem.
I also understand that the operator returns the actual type of the object, therefore in this particular case the compiler is happy.
I don't understand why the operator is called in this particular case.
Is doing function(a) the same thing as doing function(a()), only the () are implicit?
operator T() const { return toto; } is a user defined conversion operator, it is not operator(). It's used to define that your class is convertible to a different type.
operator() would look like this instead:
void operator()() const { ... }
In your case, you are using int* as T. If you substitute it yourself in the operator, you will see that it becomes operator int*() const { return toto; } which means "my class can be converted to an int* and the result of that conversion is evaluated as return toto;".
The function function() only accepts an int* as its argument. When you provide a Test instance, the call is only legal if there is a way to convert from Test to int*, which is why the operator T is required for the code to compile.
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.
Given the code below:
class A
{
public:
A(): value( 0 ) {}
int* get()
{
return &value;
}
const int& get() const
{
return value;
}
private:
int value;
};
int main()
{
A a;
const int& ref_value = a.get();
}
results in the following compilation error:
prog.cpp: In function 'int main()':
prog.cpp:23:35: error: invalid conversion from 'int*' to 'int'
const int& ref_value = a.get();
^
It seems that the overloaded get() method with const modifier does get ignored completely and the compiler sees only the non-const definition of it. It is somehow understandable since the a object is not constant. One solution would be to make the a object constant. Though there are other two solutions that makes the code compileable:
Change the signature of the const get() method by different name or other parameters added.
int* get();
const int& get_changed() const; <-- this gets called
Change the non-const get() method to return a reference instead pointer.
int& get(); <-- this gets called
const int& get() const;
though with
int* get();
const int& get() const;
we have a compiler error.
What puzzles me is the reason behind all of these behavior.
When you have both a const and non-const overload of the same function with the same parameters, which one gets called depends only on the constness of the object on which you're invoking the function. So invoking on a non-const a must call the non-const overload.
It's exactly the same situation as this:
void foo(int *p);
void foo(const int *p);
int main()
{
int i;
const int ci;
foo(&i); // Calls the first overload
foo(&ci); // Calls the second overload
}
A const-qualified function can be called on a non-const-qualified object, but that requires a "nonconst to const" conversion. If there's an overload which doesn't require such a conversion (is a better match), it will be preferred.
The reason is that the compiler can't do overload resolution based on a different return type.
Only the function signature is taken into account for overload resolution. And the function signature consists only of the function name, the parameters, and the cv qualifiers - not the return type.
I am trying to define some operators.
I do it according to this documentation:
http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html
Is there an operator that should be defined twice?
I think that it's the index operator. am I right? I defined it such as:
int operator [] (const int power) const{
// here there is some code
}
Assuming I implement it correctly, what's about the operator that should have be defined twice?
Does it support the next things?
a[1] = 3;
cout << a[1]; // I defined the << operator
any help appreciated!
I think that it's the index operator. am I right?
Almost. It is called the subscript operator, and it must accept one single argument. Your operator accepts two, and that makes your code illegal.
Does it support the next things?
Supposing you have a properly written operator [] (without knowing some context from the logic of your application I cannot tell how to write one), then both the instructions you mention should be supported.
However, in order for this:
a[1] = 3;
To be legal (if a fundamental type is returned), operator [] should return an lvalue reference - therefore, int& and not int. Of course, this means the object to which the lvalue reference is bound cannot be a local object or a temporary, because that would mean returning a dangling reference.
int& operator [] (const int power) { // <== The function cannot be "const" if you
// ^ // are returning a non-const lvalue ref
// to a data member or element of a data
// member array
// here there is some code
}
You may also want a const version of the subscript operator:
int operator [] (const int power) const {
// No need to use a int const& ^^^^^
// here, that is basically the This member function can be const, since it is
// same as returning an int by neither modifying the object on which it is
// value invoked, nor returns any non-const lvalue ref
to a data member or an element of data member
// here there is some code
}
You may want to have both a const and a non-const versions of the operator:
int operator[](int index) const { ... }
int& operator[](int index) { ... }
This will permit both usages given in your example. It will also permit the second usage even if a is const.
To support assignment there are two options
The indexing operator [] returns a reference
The indexing operator [] returns a proxy object that implements assignment
The first case is the simplest, but doesn't allow you to distinguish between read and write operations. The second method is a bit more complex but allows more control:
An example of approach (2) is the following
struct MyArray {
std::vector<int> v;
MyArray(int n) : v(n) {}
struct ItemRef {
MyArray& a;
int index;
ItemRef(MyArray& a, int index)
: a(a), index(index)
{}
int operator=(int x) {
printf("Writing to element %i\n", index);
a.v[index] = x;
return x;
}
operator int() {
printf("Reading element %i\n", index);
return a.v[index];
}
};
ItemRef operator[](int index) {
if (index < 0 || index >= int(v.size()))
throw std::runtime_error("Invalid index");
return ItemRef(*this, index);
};
};
I define two versions of overloaded operator[] function in a class array. ptr is a pointer to first element of the array object.
int& array::operator[] (int sub) {
return ptr[sub];
}
and
int array::operator[] (int sub) const {
return ptr[sub];
}
Now, if I define a const object integer1 the second function can only be called..... but if I make a non-const object and then invoke as below:
cout << "3rd value is" << integer1[2];
which function is called here?
In your second example, the non-const version will be called, because no conversion is required, and a call that requires no conversion is a better match than one that requires a conversion.
Ultimately, however, you have a basic problem here: what you really want is behavior that changes depending on whether you're using your object as an rvalue or an lvalue, and const doesn't really do that. To make it work correctly, you normally want to return a proxy object, and overload operator= and operator T for the proxy object:
template <class T>
class myarray {
T *ptr;
class proxy {
T &val;
proxy &operator=(proxy const &p); // assignment not allowed.
public:
proxy(T &t) : val(t) {}
operator T() const { return val; }
proxy &operator=(T const&t) { val = t; return *this; }
};
proxy const operator[](int sub) const { return proxy(ptr[sub]); }
proxy operator[](int sub) { return proxy(ptr[sub]); }
// obviously other stuff like ctors needed.
};
Now we get sane behavior -- when/if our array<int> (or whatever type) is const, our operator[] const will be used, and it'll give a const proxy. Since its assignment operators are not const, attempting to use them will fail (won't compile).
OTOH, if the original array<int> was not const, we'll get a non-const proxy, in which case we can use both operator T and operator=, and be able to both read and write the value in the array<int>.
Your const version should return const int& not int, so that the semantics are just the same between the two functions.
Once you've done that, it doesn't matter which one is used. If the const version has to be used because your object has a const context, then it will be... and it won't matter as you're not trying to modify anything. Otherwise, it'll use the non-const version... but with just the same effect.