问题
I am overloading operators for a data structure, so I have the standard function declarations:
T & operator[](int i); //used for regular objects
const T & operator[](int i) const; // used for const objects
So what I want to do is to have a two versions of operator[] for regular objects: one that does something different when the operator[] is used to write rather than read.
I have been reading that this is possible, but I have not yet seen any code.
I have seen many times this question asked, and I have seen the answer " 'operator[] const' version is used for reading" --> but this is not true; it is used only with const instantiations of the class.
Can anyone offer guidance on detecting the write event to trigger different behavior? Is perhaps the trick in the copy constructor?
回答1:
The class holding the objects cannot get the information whether your access to the returned object is read or write access.
Only the object itself has some notion of "in which context am I used" via the member function qualifiers.
- Ref-qualifiers
- const/volatile qualifiers
You can use this in a proxy class.
#include <vector>
#include <type_traits>
#include <iostream>
template <class T, class U = T, bool Constant = std::is_const<T>::value>
class myproxy
{
protected:
U& m_val;
myproxy& operator=(myproxy const&) = delete;
public:
myproxy(U & value) : m_val(value) { }
operator T & ()
{
std::cout << "Reading." << std::endl;
return m_val;
}
};
template <class T>
struct myproxy < T, T, false > : public myproxy<T const, T>
{
typedef myproxy<T const, T> base_t;
public:
myproxy(T & value) : base_t(value) { }
myproxy& operator= (T const &rhs)
{
std::cout << "Writing." << std::endl;
this->m_val = rhs;
return *this;
}
};
template<class T>
struct mycontainer
{
std::vector<T> my_v;
myproxy<T> operator[] (typename std::vector<T>::size_type const i)
{
return myproxy<T>(my_v[i]);
}
myproxy<T const> operator[] (typename std::vector<T>::size_type const i) const
{
return myproxy<T const>(my_v[i]);
}
};
int main()
{
mycontainer<double> test;
mycontainer<double> const & test2(test);
test.my_v.push_back(1.0);
test.my_v.push_back(2.0);
// possible, handled by "operator=" of proxy
test[0] = 2.0;
// possible, handled by "operator T const& ()" of proxy
double x = test2[0];
// Possible, handled by "operator=" of proxy
test[0] = test2[1];
}
Prints
Writing Reading Reading Writing
回答2:
As several have mentioned in the comments, you need to return a proxy, AKA smart reference, which is wrapper to the actual type T that you want, and has a link to the original object on which [] operator was called.
When the proxy is on the left side of an =, compiler will invoke its assignment operator to type T. When the proxy is on the right side of an = , compiler will invoke its cast operator to type T.
Here is a String example, I think as described in Scott Meyer's Effective C++ The first line of main should print "Writing to the String" The second line will print "Reading from the String"
struct String
{
char text[10];
struct CharReference; // forward declare char proxy class
CharReference String::operator[] ( int index );
void print() { printf("%s",text ); }
};
struct String::CharReference
{ CharReference( String * s, int i ) ;
operator char ();
const char & String::CharReference::operator = ( const char & rhs ) ;
int index;
String *theString;
};
const char & String::CharReference::operator = ( const char & rhs )
{
printf("Writing to the string\n");
theString->text[index] = rhs;
return rhs;
}
String::CharReference::operator char()
{
printf("Reading from the string\n");
return theString->text[index];
}
String::CharReference::CharReference( String * s, int i ):theString(s), index(i) {}
String::CharReference String::operator[] ( int index )
{
return CharReference( this, index);
}
int _tmain(int argc, _TCHAR* argv[])
{
String s;
s[0] = 'A';
char c = s[0];
return 0;
}
来源:https://stackoverflow.com/questions/26005383/operator-overloading-c-write-only-version