你好,
在制作一个基于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_
参数到TDerived
中TBase_
的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具有相同的效果并且不那么冗长。