指向成员函数默认值的 CRTP 相关编译器错误

发布于 2024-12-17 02:35:57 字数 3263 浏览 3 评论 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< P值,T衍生> TBase_; 正如所指出的(谢谢你们,伙计们!)。事实上,它只能在 MSVC++ 下编译,因为该编译器不进行两部分解析;即,在 codepad.org(使用 g++ 4.1.2)上它无法编译。 其次,之后,我尝试使用 static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TDerived_::Foo, TDerived_ pDerived = TDerived_() )< /code> 在 codepad.org 上并且...它编译并正确运行!所以我现在真的很困惑:人们向我解释为什么它不正确(而且我无法理解“为什么”(参见我之前的编辑)),现在事实证明 g++ 正确编译了它......这是否意味着它只是MSVC++有问题而不是代码?或者从标准的角度来看代码确实有问题(我看不到它)并且 g++“错误地”接受它(我认为不太可能)?...帮助?!

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

水晶透心 2024-12-24 02:35:57

TDerivedTBase_ 的 typedef 中的 TValue_ 参数的范围似乎是错误的(!)

您有:

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

在类或类模板的定义中,如果是基类
取决于模板参数,不检查基类范围
在非限定名称查找期间或者在定义点
类模板或成员或在类的实例化期间
模板或成员

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

久隐师 2024-12-24 02:35:57

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

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

(with TDerived_ = TDerived),这很好,因为此时声明了 TSelf_::Foo()

OTOH,问题是

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

TBaseTBase期间未声明 TDerived_::Foo() 。 int,TDerived<整数> >模板类定义实例化。

顺便说一句,您不需要将参数列表指定为 ( void ); () 具有相同的效果并且更简洁。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文