当前位置: 动力学知识库 > 问答 > 编程问答 >

c++ - CRTP-related compiler error on pointer-to-a-member-function default value

问题描述:

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?!

网友答案:

The scoping for the TValue_ parameter to the type in the typedef for TBase_ in TDerived appears to be wrong (!)

You have:

 private:
  typedef TBase< TValue_, TDerived > TBase_;

I think you need:

 private:
  typedef TBase< typename TBase< PValue, TDerived< PValue > >::TValue_, TDerived > TBase_;

Or even just:

 private:
  typedef TBase< PValue, TDerived > TBase_;

EDIT: The C++ standard section 14.6.2 para 3 covers this case:

In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member

网友答案:

Simple: when instantiating TBase< int, TDerived< int> > template class definition, the declaration of Call<> function template is instantiated:

  template< typename PType >
  static void Call( PType , void(TDerived_::*pFunction)() = &TSelf_::Foo, TDerived_ pDerived = TDerived_() )

(with TDerived_ = TDerived< int>), which is fine as TSelf_::Foo() is declared at this point.

OTOH, the problem with

static void Call( PType , void(TDerived_::*pFunction)() = &TDerived_::Foo, TDerived_ pDerived = TDerived_() )

is that TDerived_::Foo() is not declared during TBase< int, TDerived< int> > template class definition instantiation.

BTW, you don't need to specifiy a parameter list as ( void ); () has the same effect and is less verbose.

分享给朋友:
您可能感兴趣的文章:
随机阅读: