指向成员函数默认值的 CRTP 相关编译器错误
您好,
在制作基于 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
TDerived
中TBase_
的 typedef 中的TValue_
参数的范围似乎是错误的(!)您有:
我认为您需要:
或者甚至只是:
编辑:C++标准第14.6.2第3段涵盖了这种情况:
The scoping for the
TValue_
parameter to the type in the typedef forTBase_
inTDerived
appears to be wrong (!)You have:
I think you need:
Or even just:
EDIT: The C++ standard section 14.6.2 para 3 covers this case:
简单:实例化
TBase<时int,TDerived<整数> >
模板类定义,实例化Call<>
函数模板的声明:(with
TDerived_
=TDerived
),这很好,因为此时声明了TSelf_::Foo()
。OTOH,问题是
在
TBase
TBase
期间未声明
模板类定义实例化。TDerived_::Foo()
。 int,TDerived<整数> >顺便说一句,您不需要将参数列表指定为
( void )
;()
具有相同的效果并且更简洁。Simple: when instantiating
TBase< int, TDerived< int> >
template class definition, the declaration ofCall<>
function template is instantiated:(with
TDerived_
=TDerived< int>
), which is fine asTSelf_::Foo()
is declared at this point.OTOH, the problem with
is that
TDerived_::Foo()
is not declared duringTBase< 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.