2
votes

I can't figure out what I am doing wrong. Problem in the template deduction from pointer to base-class member - &DerivedClass::BaseClassMemeber.

Full example:

#include <vcl.h>
#include <tchar.h>

struct Base
{
   int BaseClassMember;
};

struct Derived : public Base
{
};

template<class T1, class T2>
void Test(T1& Param1, T2 T1::* Param2)
{
}

int _tmain()
{
   Derived inst;
   // Compile error E2285 Could not find a match for 'Test<T1,T2>(B,int A::*) - BCC32
   // Error 1 error C2782: 'void Test(T1 &,T2 T1::* )' : template parameter 'T1' is   ambiguous - MS VS8
   Test(inst, &Derived::BaseClassMember);

   // Works great
   Test<Derived>(inst, &Derived::BaseClassMember);
   return 0;
 }

I can found several workarounds, e.g. Additional Test function overloading with one more template parameter, static_cast, implicit partial specialization (Test).

But i am interested in reasons why complier just can't use class which was explicitly specified in &DerivedClass::BaseClassMemeber. That is the question. And if you have more elegant solution for problem, welcome.

1
You may also find it interesting your "Works great" doesn't on clang 3.3. Test<Base>, however, does.WhozCraig
Ok this excludes one workaround, but nevertheless where is the problem?skondratov
For your workaround with another template parameter, I'd say implement it as template<class T1, class T2, class T3> typename std::enable_if<std::is_convertible<T2 T3::*, T2 T1::*>::value, void>::type Test(T1& Param1, T2 T3::* Param2), or if C++11 is an option, template<class T1, class T2, class T3> auto Test(T1& Param1, T2 T3::* Param2) -> decltype(Param1.*Param2, void()). They will help you if you add additional overloads of Test, where you want overload resolution to ignore invalid attempts calls to this particular Test (Test(inst, &Unrelated::ClassMember);).user743382
Didn't know about std::enable_if, thanks. Unfortunately c++11 is not an option.skondratov

1 Answers

2
votes

&Derived::BaseClassMember has type int Base::*, not int Derived::*. Quoth the Standard:

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m. [... skip ...] [Example:

struct A { int i; };
struct B : A { };
... &B::i ...             // has type int A::*i

— end example]

You have to cast the value to int Derived::* if you need this type.