0

你好呀,

在制作基于 CRTP 的通用包装器来调用任意库函数时,我遇到了一个我无法理解的问题。这是一个非常简化的代码来说明问题:

#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 );
}

一切都按预期编译和工作。但是,如果我尝试使用指针TDerived::Foo()作为第二个参数的默认参数TBase::Call(...)

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

编译器给出了一个语法错误......我感觉它与编译器如何解析代码有关,并且它无法找出指向尚未定义(或实例化)类的函数的指针。TDerived但是,调用构造函数作为第三个参数的默认参数是没有问题的TBase::Call(...)。有人可以就发生的事情给我一个明确的答案吗?为什么派生类 MFP 不被接受,而派生类的对象被接受为默认参数?

谢谢。

编辑:编译器的错误(MSVS2010 命令行编译器):

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

这是一个语法错误 - 它不能识别TDerived_为 MFP 的默认参数中的类型。这之后还有其他错误,它们都是语法错误,因为函数定义现在格式错误。我就是这么理解的。

编辑:基本上,我不明白为什么我可以使用对象TDerived_作为默认参数,但不能使用指向成员函数的指针作为默认参数。

编辑:好的,这让我发疯了。首先,我改变了typedef TBase< PValue, TDerived > TBase_;它所指出的(谢谢你们,伙计们!)。事实上,它只在 MSVC++ 下编译,因为这个编译器不做两部分解析;即,在codepad.org(使用g++ 4.1.2)上它没有编译。其次,在那之后,我尝试static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TDerived_::Foo, TDerived_ pDerived = TDerived_() )在codepad.org上使用......它编译并正确运行!所以我现在真的很困惑:人们向我解释了为什么它不正确(而且我无法理解“为什么”(参见我以前的编辑)),现在事实证明 g++ 可以正确编译它......这是否意味着它只是MSVC++ 问题而不是代码?或者从标准的角度来看代码确实存在问题(我看不到它)并且g ++“错误地”接受它(我认为不太可能)?..帮助?!

4

2 回答 2

2

TValue_in 的 typedef 中类型TBase_的参数范围TDerived似乎是错误的(!)

你有:

 private:
  typedef TBase< TValue_, TDerived > TBase_;

我认为你需要:

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

甚至只是:

 private:
  typedef TBase< PValue, TDerived > TBase_;

编辑:C++ 标准第 14.6.2 节第 3 段涵盖了这种情况:

在类或类模板的定义中,如果基类依赖于模板参数,则在非限定名称查找期间,无论是在类模板或成员的定义点还是在类模板或成员的实例化期间,都不会检查基类范围类模板或成员

于 2011-11-19T05:55:49.923 回答
1

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.

于 2011-11-19T07:32:43.843 回答