Hi there,
While making a CRTP-based generic wrapper to call arbitrary library functions, I've encountered a problem which I have trouble understanding. Here is a very simplified code to illustrate the problem:
#include <iostream>
template< typename PValue, typename PDerived >
class TBase
{
private:
typedef TBase TSelf_;
typedef PDerived TDerived_;
protected:
typedef PValue TValue_;
protected:
TBase( void )
{
std::cout << " TBase::TBase() " << std::endl;
}
public:
void Foo( void )
{
std::cout << " TBase::Foo() " << std::endl;
}
template< typename PType >
static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TSelf_::Foo, TDerived_ pDerived = TDerived_() )
{
( pDerived.*pFunction )();
std::cout << " static TBase::Call(). " << std::endl;
}
};
template< typename PValue >
class TDerived : public TBase< PValue, TDerived< PValue > >
{
friend class TBase< PValue, TDerived< PValue > > ;
private:
typedef TBase< PValue, TDerived > TBase_;
typedef TDerived TSelf_;
public:
TDerived( void ) :
TBase_()
{
std::cout << " TDerived::TDerived() " << std::endl;
}
void Foo( void )
{
std::cout << " TDerived::Foo() " << std::endl;
}
void Bar( void )
{
std::cout << " TDerived::Bar() " << std::endl;
}
};
int main( void )
{
TDerived< int >::Call( 1 );
TDerived< int >::Call( 1, &TDerived< int >::Foo );
TDerived< int >::Call( 1, &TDerived< int >::Bar, TDerived< int > () );
return ( 0 );
}
Everything compiles and works as intended. However, if I try to use pointer to TDerived::Foo()
as a default argument for the second parameter in TBase::Call(...)
:
static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TDerived_::Foo, TDerived_ pDerived = TDerived_() )
compilers gives a syntax error... I have a feeling it is related to how compiler parses code and that it cannot figure out pointer to a function of yet to be defined (or instantiated) class. However, it has no problem calling TDerived
constructor as a default argument for the third parameter of TBase::Call(...)
. Can someone give me a definite answer about what's going on? Why derived class MFP is not accepted, and object of derived class is accepted as default arguments?
Thanks.
EDIT: compiler's error (MSVS2010 command line compiler):
FMain.cpp(224) : error C2061: syntax error : identifier 'TDerived_'; FMain.cpp(233) : see reference to class template instantiation 'TBase<PValue,PDerived> with [PValue=int,PDerived=TDerived<int>]' being compiled; FMain.cpp(323) : see reference to class template instantiation 'TDerived<PValue> with [PValue=int]' being compiled
It's a syntax error - it does not recognize TDerived_
as type in default argument for MFP. There are other errors following this one, they are all syntax errors, since function definition is ill-formed now. That is how I understand it.
EDIT: Basically, I don't understand why can I use an object of TDerived_
as a default argument, but can not use a pointer to a member function as a default argument.
EDIT: Ok, this is driving me crazy now.
First of all, I changed to typedef TBase< PValue, TDerived > TBase_;
as it was pointed out (thank you, guys!). Indeed, it only compiled under MSVC++, since this compiler does not do two-part parsing; i.e., on codepad.org (which uses g++ 4.1.2) it didn't compile.
Second, after that, I tried to use static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TDerived_::Foo, TDerived_ pDerived = TDerived_() )
on codepad.org and... it compiled and run correctly! So I'm REALLY confused now: people explained to me why it's not correct (and I couldn't understand "why" (see my previous EDIT)) and now it turns out g++ compiles it correctly... Does it mean it just MSVC++ problem and not the code? Or code does have a problem from the Standard point of view (and I cannot see it) and g++ accept it "by mistake" (unlikely, I think)?.. Help?!