2011-11-19 79 views
0

您好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); 
} 

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

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),它没有编译。 其次,在那之后,我尝试在codepad.org上使用static void Call(PType /*pSomething*/, void(TDerived_::*pFunction)(void) = &TDerived_::Foo, TDerived_ pDerived = TDerived_()),并且......编译并正确运行!所以我现在真的很困惑:人们向我解释为什么它不正确(我不明白“为什么”(参见我之前的编辑)),现在事实证明g ++编译它是正确的......这是否意味着它只是MSVC++的问题,而不是代码?或者,从标准的角度来看,代码确实存在问题(我看不到它),并且g ++“错误地”接受它(不太可能,我想)?帮助?!

+0

编译器给出了什么错误?你为什么不发布这个呢?是否希望我们编译此代码并自己查看错误? – Nawaz

+0

“语法错误”错误消息是可能的最不有用的诊断消息(除“某处出现错误”)。 – curiousguy

+0

回复:您最近的评论 - 在typedef更改后,代码对我来说很合适,但也许对标准有更广泛认识的人可以发表评论。 FWIW,在VS2008和GCC 4.2.1上编译并运行后, – msandiford

回答

2

TValue_参数在TDerived中的typedef TBase_类型的作用域似乎是错误的

你有(!):

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<>功能TE的声明时:的 类模板或构件或定义的点的类 模板或构件

+0

'TValue_'在'TBase'中定义为受保护的'typedef'。 “TDerived”从“TBase”公开继承并可以访问其受保护的定义。 – lapk

+0

@AzzA实际上msandiford并没有说“TValue_'在这种情况下无法访问,他说它没有正确的”范围“。他的意思是'TValue_' **在这里不可见**。 – curiousguy

+0

有关为什么'TValue_' **不能**在C++ **的派生类**中可见的解释,请参阅[在析构函数中使用此关键字(关闭)](http://stackoverflow.com/questions/7908248/using-this-keyword-in-destructor/7908530#7908530) – curiousguy

1

简单的实例化期间mplate被实例化:

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_()) 

问题是,TDerived_::Foo()没有在TBase< int, TDerived< int> >模板类的定义实例声明。

顺便说一句,你不需要指定参数列表(void); ()具有相同的效果并且不太冗长。

+0

是的,但第三个默认参数呢?为什么我可以使用'TDerived_'作为第三个默认参数?如果我不能使用'TDerived_'成员函数的地址,我如何在同一个作用域中创建一个'TDerived_'对象?我一定很愚蠢,但我仍然不明白... – lapk

+0

我想,我需要得到一个遵循标准的编译器。当编译器从根本上允许不按照标准编译的代码时,它可能毫无意义地询问“为什么编译和不编译”。感谢您的投入,伙计们。 – lapk

+0

@AzzA“_为什么我可以使用'TDerived_'临时作为第三个默认参数?_”知道'TDerived_'是一个类型,'TDerived _()'的含义是已知的:构造一个类型为TDerived_'的临时对象。当然,只有一个构造函数可以接受0个参数,并且可以在此上下文中访问,但必须稍后再进行检查**,但这不会影响表达式是右值键入'TDerived_'。 OTOH编译'TDerived _ :: Foo',编译器需要能够查找名称以确定表达式的类型。 – curiousguy