Overloading Member Access Operators -≫, .*

Overloading member access operators ->, .*

->

This is the only really tricky one. It must be a nonstatic member function, and it takes no arguments. The return value is used to perform the member lookup.

If the return value is another object of class type, not a pointer, then the subsequent member lookup is also handled by an operator-> function. This is called the "drill-down behavior." The language chains together the operator-> calls until the last one returns a pointer.

struct client
{ int a; };

struct proxy {
client *target;
client *operator->() const
{ return target; }
};

struct proxy2 {
proxy *target;
proxy &operator->() const
{ return * target; }
};

void f() {
client x = { 3 };
proxy y = { & x };
proxy2 z = { & y };

std::cout << x.a << y->a << z->a; // print "333"
}

->*

This one is only tricky in that there is nothing special about it. The non-overloaded version requires an object of pointer to class type on the left-hand side and an object of pointer to member type on the right. But when you overload it, you can take any arguments you like and return anything you want. It doesn't even have to be a nonstatic member.

In other words, this one is just a normal binary operator like +, -, and /. See also: Are free operator->* overloads evil?

.* and .

These cannot be overloaded. There is already a built-in meaning when the left-hand side is of class type. Perhaps it would make a little sense to be able to define them for a pointer on the left-hand side, but the language design committee decided that would be more confusing than useful.

Overloading ->, ->*, ., and .* can only fill in cases where an expression would be undefined, it can never change the meaning of an expression that would be valid with no overloading.

Overloading '->' member access operator

C++ does not work like search/replace in a text editor. "obj1->" is not replaced by ptr verbatim, as if this was a line of text in a text file.

With:

Obj->myfun();

The fact that the -> operator invokes a custom operator that returns ptr:

T * operator -> () { return ptr; }

That doesn't mean that the original statement somehow becomes

ptrmyfun();

As if "Obj->" was replaced by "ptr". C++ simply does not work this way, like search/replace in a text editor.

The -> operator results in the custom operator->() getting invoked. That part, of how you understand operator overloading works, is correct.

But what happens is that, simply, the return value from the operator->() becomes the actual pointer value to which the actual pointer dereference gets applied to.

So this becomes, essentially:

ptr->myfun();

That's all.

Overload -> operator to forward member-access through Proxy

If you can change Object, you may add

class Object {
public:
// other code
const Object* operator -> () const { return this; }
Object* operator -> () { return this; }
};

And for your Proxy

Object operator->() { return container[key]; }

So, for example

myObj[42]->myFoo = ...

is mostly equivalent to

Proxy proxy = myObj[42];
Object obj = proxy.operator ->();
Object* pobj = obj.operator ->(); // so pobj = &obj;
pobj->myFoo = ...

overload -> (member access) recursively

I cannot find an example of a real use of it ( unless it is used to find a linked list last element ).

I think you're misunderstanding what it does. It isn't used to dereference a list element and keep dereferencing the next element. Each time you call operator-> you would get back a different type, the point is that if that second type also has an operator-> it will be called, which might return a different type again. Imagine it being like x->->->i not x->next->next->next if that helps

An example of real world use could help too, as probably I am missing a model where to apply the behaviour.

It can be useful for the Execute Around Pointer pattern.

On the opposite side there could be the need to avoid that behaviour, how could it be done ?

Call the operator explicitly:

auto x = p.operator->();

Overloading operator ->* in C++

For completeness, here's a complete, compilable, minimal example, heavily inspired by this paper I've linked to, and stripped down along with a small usage demo in order to get you started with this:

#include <memory>
#include <iostream>
#include <utility>


// Our example class on which we'll be calling our member function pointer (MFP)
struct Foo {
int bar() {
return 1337;
}
};

// Return value of operator->* that represents a pending member function call
template<typename C, typename MFP>
struct PMFC {
const std::unique_ptr<C> &ptr;
MFP pmf;
PMFC(const std::unique_ptr<C> &pPtr, MFP pPmf) : ptr(pPtr), pmf(pPmf) {}

// the 'decltype' expression evaluates to the return type of ((C*)->*)pmf
decltype((std::declval<C &>().*pmf)()) operator()() {
return (ptr.get()->*pmf)();
}
};

// The actual operator definition is now trivial
template<typename C, typename MFP>
PMFC<C, MFP> operator->*(const std::unique_ptr<C> &ptr, MFP pmf)
{
return PMFC<C, MFP>(ptr, pmf);
}

// And here's how you use it
int main()
{
std::unique_ptr<Foo> pObj(new Foo);
auto (Foo::*pFn)() = &Foo::bar;
std::cout << (pObj->*pFn)() << std::endl;
}

C++ override member access operator for template class and return by reference

If you return a reference, you can't use it in ref->do_something(); which requires a pointer. You'd have to use this cumbersome method:

ref.operator->().do_something(); 

Instead return a pointer - and make it a T* (or const T*), not a Ref<T>*.

Example:

#include <iostream>

class A {
public:
void do_something() {
std::cout << "Hey there\n";
}
};

template<class T>
class Ref {
public:
Ref(T& ptr) : objPtr(&ptr) {} // taking a T& but storing a pointer

const T* operator->() const { return objPtr; }
T* operator->() { return objPtr; }

private:
T* objPtr;
};

int main() {
A myObj;
Ref<A> ref(myObj);

ref->do_something();
}

C++: Overloading the [ ] operator for read and write access

Your mutable version is fine:

T& operator[](T u);

but the const version should be a const member function as well as returning a const reference:

const T& operator[](T u) const;
^^^^^

This not only distinguishes it from the other overload, but also allows (read-only) access to const instances of your class. In general, overloaded member functions can be distinguished by their parameter types and const/volatile qualifications, but not by their return types.

Making overloaded member access operator in C++ return a temporary

Is there a way to make this work?

No. Value category is a property of an expression, not of a type. "pointer to prvalue" is not a thing

Overloading the class subscript operator to access elements of a member std::vector object

TartanLlama's and songyuanyao's answers are correct only when the contained variable type (i.e.; ValueType) is std::vector. If we attempt to store a fundamental data type (e.g.; int or float), the compiler (MSVC14) gives the error below since there won't be any implicit subscript operator operator[] or value_type member type definition inside.

'InputFileVariable<bool,std::string>::value_type': is not a type name, static, or enumerator  
'InputFileVariable<int,std::string>::value_type': is not a type name, static, or enumerator
'InputFileVariable<uintmax_t,std::string>::value_type': is not a type name, static, or enumerator
'InputFileVariable<float,std::string>::value_type': is not a type name, static, or enumerator

I found the solution by using function templates. I rewrote the subscript operators as templates, so that the compiler doesn't create the subscript member functions unless they are called. And since I call them only when the stored element is an std::vector, it doesn't cause any problem with fundamental types.

My working final code is below.

#include <vector>
#include <string>

template<class ValueType, class KeyType = std::string>
class InputFileVariable
{
public:
const KeyType Key;
ValueType Value;
bool Exists;
InputFileVariable(KeyType && Key, ValueType && Value, bool Existance = false)
: Key (std::forward<KeyType> (Key)),
Value (std::forward<ValueType>(Value)),
Exists (Existance)
{
}
size_t size() const
{
return Value.size();
}
const InputFileVariable & operator=(InputFileVariable && Another)
{
Key = std::forward<InputFileVariable>(Another).Key;
Value = std::forward<InputFileVariable>(Another).Value;
Exists = true;
return *this;
}
template <class ElementType = ValueType::value_type>
const typename ElementType & operator[](size_t Index) const
{
return Value[Index];
}
template <class ElementType = ValueType::value_type>
typename ElementType & operator[](size_t Index)
{
return Value[Index];
}
operator const ValueType & () const
{
return Value;
}
operator ValueType & ()
{
return Value;
}
};

int wmain(int argc, wchar_t *argv[], wchar_t *envp[])
{
// Used with "std::vector":
InputFileVariable<std::vector<int>> MyVar1("MV1", {2, 4, 6, 8}, true);
const size_t SIZE = MyVar1.size();
std::cout << "Size = " << SIZE << std::endl;
int Temp = MyVar1[1];
MyVar1[1] = MyVar1[2]; // Here we call both the const and non-const operators.
MyVar1[2] = Temp;
for (size_t i=0; i<SIZE; i++)
{
std::cout << "MyVar1[" << i << "] = " << MyVar1[i] << std::endl;
}

// Used with "double":
InputFileVariable<double> MyVar2("MV2", 3.14, true);
std::cout << std::endl << "MyVar2 = " << MyVar2 << std::endl;

std::cout << std::endl;
_wsystem(L"timeout /t 60 /nobreak");
return 0;
}

Output:

Size      = 4
MyVar1[0] = 2
MyVar1[1] = 6
MyVar1[2] = 4
MyVar1[3] = 8

MyVar2 = 3.14


Related Topics



Leave a reply



Submit