4

What does operator.*() do? What is it's purpose?

It is documented as Pointer-to-member and exactly the same as ->*. Are those two identical?

Scott Meyers in More effective C++, Item 7 explains that .* is NOT overloadable, while ->* IS. Why is that?

Vorac
  • 8,726
  • 11
  • 58
  • 101
  • 12
    `.*` is to `->*` what `.` is to `->`. – T.C. Feb 25 '15 at 13:12
  • 1
    That page just says it has the same precedence as `->*`, not that it's "exactly the same". It's similar, but applied to objects/references like `.`, rather than pointers like `->`. – Mike Seymour Feb 25 '15 at 13:16
  • @hvd No, the OP is right. These operators do exist. [This MSDN page](https://msdn.microsoft.com/en-us/library/k8336763.aspx) describes how they are used and how they differ – Panagiotis Kanavos Feb 25 '15 at 13:16
  • 2
    @PanagiotisKanavos That seems like a non sequitur. I never claimed the operators don't exist. –  Feb 25 '15 at 13:17
  • 1
    @5gon12eder C++ really does have `.*` and `->*` operators, where `*` is part of the operator syntax. –  Feb 25 '15 at 13:21
  • 1
    @5gon12eder No, no. It’s not a placeholder at all, and `.*` and `->*` *are* real operators. – Konrad Rudolph Feb 25 '15 at 13:21
  • @hvd Oops, I got to do some reading. Thanks. I have always parsed these constructs as `.(*x)` pr `->(*x)` but it seems that this was wrong. – 5gon12eder Feb 25 '15 at 13:23

4 Answers4

3

As answered already, no, .* and ->* don't mean the same thing. Assuming no overloaded operators are used, a->*b means (*a).*b, i.e. .* is used with class types, and ->* is used with pointers to class types. It's just like how a->b means (*a).b if the built-in operators are used.

Neither . nor .* is overloadable because it was not clear when the standard was written whether that would have negative consequences. . is the more useful of these, and is still being considered for a future version of C++, but the first proposal of making that overloadable dates back to 1990 (well before the first C++ standard was published). Some of the issues that need to be resolved involve choosing what to do when a.b is valid with the built-in operator, but not valid as (pseudo-code) (a.operator.()).b. Does the built-in operator then get used in its place, or should this be an error? Neither seems particularly desirable.

Some of the proposals for overloading the . operator also include overloading the .* operator, others don't. There isn't all that much demand for making .* overloadable by itself, so if it does get accepted at some point, it'll probably happen along with ., and that's been taking a very long time.

  • Were it possible, I'd expect it to be much like overloading `&` : one-way ticket to getting fired :p – Quentin Feb 25 '15 at 14:32
  • @Quentin [There are a few good uses of overloaded `operator&`](http://stackoverflow.com/questions/6495977/what-legitimate-reasons-exist-to-overload-the-unary-operator), and when the built-in operator is needed, it's possible to get the address without relying on `&`, just like it'd be possible to call a member function directly on an object even if `operator.` is overloaded. (You'd be able to write `addressof(obj)->fun()` instead of `obj.fun()`.) –  Feb 25 '15 at 14:40
2

it's designed to be used with :

left operand of class type

right operand of pointer to a "member of this class type"

#include <iostream>
#include <string>
using std::string;

class Foo{
public:
  int f(string str){
    std::cout<<"Foo::f()"<<std::endl;
    return 1;
  }
};

int main(int argc, char* argv[]){
  int (Foo::*fptr) (string) = &Foo::f;
  Foo obj;
  (obj.*fptr)("str");//call: Foo::f() through an object
  Foo* p=&obj;
  (p->*fptr)("str");//call: Foo::f() through a pointer
}

note that i didn't produce this code, it comes from a tutorial which explained how it works, but not actually what's the purpose

the difference about overloadability is the same as between . and ->, so it's not particular to this case and there's been subject about this like this one

commitee are deciding those kinds of things, not everytimes with obvious reason, but this is coherent with the fact that x-> can be seen as (*x)., .() cannot be overload, but *() can be, so the combination of those implies that -> can be overload, because "one part of it with a different writing" can be overload

what i say last is just my mind trying to go on admiring c++ for his beauty and coherence

Community
  • 1
  • 1
Guiroux
  • 531
  • 4
  • 11
0

Its a pointer a member. Example:

// This functions waits until predicate evalues false
void waitSomething(bool (one_class::*predicate)(),one_class& that)
{
    while ((that.*predicate)())
    {
        sleep(100);
    }
}

You can call as:

one_class a;
waitSomething(&a::*predicate,a); //predicate is an internal function member

You can use for attributes too.

->* is for pointers:

// This functions waits until predicate evalues false
void waitSomething(bool (one_class::*predicate)(),one_class* that)
{
    while ((that->*predicate)())
    {
        sleep(100);
    }
}
amchacon
  • 1,891
  • 1
  • 18
  • 29
-1

I'm not sure if this will be particularly helpful for you but this is what I have learned thanks to your question.

A reasonable use-case for overloading operator ->* (which is a real thing…) alongside with operator -> could be to provide transparent access to a managed object (thinking of std::unique_ptr) also for indirect member access. Note, however, that std::unique_ptr overloads operator -> but not operator ->*.

Like we cannot overload operator ., we also cannot overload operator .* because it was decided to be like this.

So here is a somewhat silly class that wraps an object and pretends to be a pointer to it. The example would make more sense if the managed object were held by a pointer with managed life-time but I wanted to avoid cluttering the example with resource management code that is unrelated to member access.

// C++14

#include <iostream>
#include <utility>
#include <vector>

template<typename T>
class PointerLike
{

private:

  T object_;

public:

  template<typename... ParamT>
  PointerLike(ParamT&&... params) : object_ {std::forward<ParamT>(params)...}
  {
  }

  // The 'const' overloads have been omitted for the sake of brevity.

  // "Dereferences" the handle and obtains a reference to the managed object.
  T&
  operator*()
  {
    return this->object_;
  }

  // Accesses a member of the managed object.
  T *
  operator->()
  {
    return &(this->object_);
  }

  // Indirectly invokes a function member of the managed object.
  template<typename RetT, typename... ArgT>
  decltype(auto)
  operator->*(RetT(T::*mfunc)(ArgT...))
  {
    return [=](ArgT... args)->RetT{
      return (object_.*mfunc)(std::forward<ArgT>(args)...);
    };
  }
};

int
main()
{
  typedef std::vector<int> ivec;
  typedef void (ivec::*append_member_type)(int&&);
  append_member_type append = &ivec::push_back;
  PointerLike<ivec> pl {1, 2, 3, 4};
  pl->push_back(5);    // pointer-like direct member access
  (pl->*append)(6);    // pointer-like indirect member access ("natural" way)
  ((*pl).*append)(7);  // pointer-like indirect member access
  for (const auto& v : *pl)
    std::cout << v << std::endl;
}

The program will output the integers from 1 to 7.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92