指针对成员函数默认值出现与CRTP相关的编译器错误

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

本文关键字:CRTP 错误 编译器 成员 函数 默认值 指针      更新时间:2023-10-16

你好,

在制作一个基于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不被接受,而派生类的对象被接受为默认参数?

谢谢。

EDIT:编译器错误(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的默认参数中的类型。这之后还有其他错误,它们都是语法错误,因为现在函数定义格式不正确。这就是我的理解。

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

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

TValue_参数到TDerivedTBase_的typedef中的类型的作用域似乎是错误的(!(

您有:

 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段涵盖了这种情况:

在类或类模板的定义中,如果基类取决于模板参数,基类作用域不被检查在定义类模板或成员,或者在类的实例化期间模板或成员

简单:实例化TBase< int, TDerived< int> >模板类定义时,实例化Call<>函数模板的声明:

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

(其中TDerived_TDerived< int>(,这与此时声明的TSelf_::Foo()一样好。

OTOH,的问题

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

在CCD_ 18模板类定义实例化期间未声明CCD_。

顺便说一句,您不需要将参数列表指定为( void );CCD_ 20具有相同的效果并且不那么冗长。