-1

Here is the full code which compiles and runs fine:

 # include <iostream>

using namespace std;

template<class T> class A { };

template<int i> class B { };

class C {
   public:
      int x;
};

class D {
   public:
      C y;
      int z;
};

template<class T> void f (T)          { cout << "T" << endl; };
template<class T> void f1(const T)    { cout << "const T" << endl; };
temlate<class T> void f2(volatile T) { cout << "volatile T" << endl;  };
template<class T> void g (T*)         { cout << "T*" << endl; };
template<class T> void g (T&)         { cout << "T&" << endl; };
template<class T> void g1(T[10])      { cout << "T[10]" << endl;};
template<class T> void h1(A<T>)       { cout << "A<T>" << endl; };

void test_1() {
   A<char> a;
   C c;

   f(c);   f1(c);   f2(c);
   g(c);   g(&c);   g1(&c);
   h1(a);
}

template<class T>          void j(C(*)(T)) { cout << "C(*) (T)" << endl; };
template<class T>          void j(T(*)())  { cout << "T(*) ()" << endl; }
template<class T, class U> void j(T(*)(U)) { cout << "T(*) (U)" << endl; };

void test_2() {
   C (*c_pfunct1)(int);
   C (*c_pfunct2)(void);
   int (*c_pfunct3)(int);
   j(c_pfunct1);
   j(c_pfunct2);
   j(c_pfunct3);
}

template<class T>          void k(T C::*) { cout << "T C::*" << endl; };
template<class T>          void k(C T::*) { cout << "C T::*" << endl; };
template<class T, class U> void k(T U::*) { cout << "T U::*" << endl; };


void test_3() {
   k(&C::x);
   k(&D::y);
   k(&D::z);
}

template<class T>     void m(T (C::*)() )
   { cout << "T (C::*)()" << endl; };
template<class T>     void m(C (T::*)() )
   { cout << "C (T::*)()" << endl; };
template<class T>     void m(D (C::*)(T))
   { cout << "D (C::*)(T)" << endl; };
template<class T, class U>  void m(C (T::*)(U))
   { cout << "C (T::*)(U)" << endl; };
template<class T, class U>  void m(T (C::*)(U))
   { cout << "T (C::*)(U)" << endl; };
template<class T, class U>  void m(T (U::*)() )
   { cout << "T (U::*)()" << endl; };
template<class T, class U, class V> void m(T (U::*)(V))
   {
 cout << "T (U::*)(V)" << endl; };

void test_4() {
   int (C::*f_membp1)(void);
   C (D::*f_membp2)(void);
   D (C::*f_membp3)(int);
   m(f_membp1);
   m(f_membp2);
   m(f_membp3);

   C (D::*f_membp4)(int);
   int (C::*f_membp5)(int);
   int (D::*f_membp6)(void);
   m(f_membp4);
   m(f_membp5);
   m(f_membp6);

   int (D::*f_membp7)(int);
   m(f_membp7);
}

template<int i> void n(C[10][i]) { cout << "E[10][i]" << endl; };
template<int i> void n(B<i>)     { cout << "B<i>" << endl; };

void test_5() {
   C array[10][20];
   n(array);
   B<20> b;
   n(b);
}

template<template<class> class TT, class T> void p1(TT<T>)
   { cout << "TT<T>" << endl; };
template<template<int> class TT, int i>     void p2(TT<i>)
   { cout << "TT<i>" << endl; };
template<template<class> class TT>          void p3(TT<C>)
   { cout << "TT<C>" << endl; };

void test_6() {
   A<char> a;
   B<20> b;
   A<C> c;
   p1(a);
   p2(b);
   p3(c);
}

int main() { test_1(); test_2(); test_3(); test_4(); test_5(); test_6(); }

The cause of all problems in my life and brain is: test_3()

Related code for easier reading:

class C {
   public:
      int x;
};

template<class T>          void k(T C::*) { cout << "T C::*" << endl; };
template<class T>          void k(C T::*) { cout << "C T::*" << endl; };
template<class T, class U> void k(T U::*) { cout << "T U::*" << endl; };

void test_3() {
   k(&C::x);
   k(&D::y);
   k(&D::z);
}

This piece of code annoying me the most:

template<class T>          void k(T C::*)

I mean what sort of syntax is that and how it works fine. Why we need T before C::* or C before T::* vice versa. Please help me and somebody tell me why that syntax is so weird like that and how it works.

I am new to C++ and have good experience in C#, C and OOPs. Kindly explain the syntax and any alternate ways/syntaxes to write the above lines of code in a cleaner way if any. Thank you in advance.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324

2 Answers2

5

T C::* declares a pointer to any member of the class C that has the type T (where T is a template-parameter). The parameter has no name, and thus cannot be used inside the function. It is only use for overload-resolution. The same goes for C T::* and T U::*.

The reason why it works can be seen from the instantiations:

k(&C::x);

This resolves to the first overload, and T will be inferred to have the type int, because C::x is of type int.

k(&D::y);

This resolves to the second overload, because D::y is of type C. T will be inferred to have type D.

k(&D::z);

This resolves to the third overload. T will be inferred to have type D and U will be inferred to have type int, because D::z is of type int.

All in all I'd say that this example was probably designed to confuse people. If it was meant as a tutorial, it could do with better naming and some comments.

Community
  • 1
  • 1
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • Awesome, thank you so very much Björn Pollex, I am so happy now. The word annoyed was not because of rude attitude and I am sorry if anybody thought it was rude. It was just that I am new to C++ but with quite a good c# and C experience but still was not able to understand it and was stuck so basically it was frustration while self studying. Thanks to all of you guys who took your precious time to help me. You guys rock... – Rajesh Patil Dec 07 '12 at 11:14
  • Ya Björn Pollex perfectly a valid point i.e. This paragraph: All in all I'd say that this example was probably designed to confuse people. If it was meant as a tutorial, it could do with better naming and some comments. There are no coding convention used in that example there, leave alone comments but not even meaningful naming. But no complaints because apart from this example there were some very easy to understand examples in that online tutorial. I can only blame myself for not able understand and as already mentioned above consider me a C++ newbie. – Rajesh Patil Dec 07 '12 at 11:21
2

It looks like that program demonstrates various cases of overload resolution. It's just exercising the grammar.

T C::* declares an object which refers to a member of class C having the type T. To refer to a member of a class, you need both the type of the class and the type of the member. The :: operator in the declaration corresponds to the one used in initialization:

T C:: *x = & C::q;

Here the * pointer declarator matches the address-of operator &, similarly to declaring and initializing a pointer T *y = & r;. An object referring to a class member is called a pointer to member as it looks like a pointer in the syntax. (But under the hood it's not a pointer at all.)

Pointer to members are rarely used, and you can safely ignore them until you're more familiar with C++.

This doesn't have anything in particular to do with templates, the program is just using a pointer to member because it involves two underlying types T and C, and template overload resolution chooses one of the functions based on how specifically the T and C in the signature match the T and C parts of the argument type.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421