泛型编程使用gcc编译器时的两个注意事项:关于typename和迭代器

    其实,我想说的是,在使用模板类时要注意一点,即何时必须使用typename而不是class来指定模板类型形参。其实在C++神作《C++ Primer (特别版)》的第16章“模板和泛型编程”中已经说得很明白了。下面,我结合自己遇到的问题来讲讲。

template <class ElemType>
bool Complete (TreeNode <ElemType>* t)
{
    ......
    ......
    for (deque <TreeNode <ElemType>*> :: iterator iter = q.begin (); iter + 1 < q.end (); iter ++)
    {
    ......
    }
}


    上面这段代码在codeblocks里面写的,编译器是gcc。编译后错误提示为:

D:\programming practice\acm practice\6-36\treelib.h|100|error: expected ';' before 'iter'|
D:\programming practice\acm practice\6-36\treelib.h|100|error: 'iter' was not declared in this scope|

    这个提示非常坑爹:我找了一下午也没有找到哪里少了个分号,至于'iter'没有定义则更是无稽之谈。最终,维基百科和《C++ Primer (特别版)》帮了我的忙。下面我把该书P631的解释完整抄写到这里,希望大家看得明白:

1.typename与class的区别

    在函数模板形参表中,关键字typename和class具有相同含义,可以互换使用,两个关键字都可以在同一模板形参表中使用:

template <typename T, class U> calc (const T&, const U&);


使用关键字typename代替关键字class指定模板类型形参也许更为直观。毕竟可以使用内置类型(非类类型)作为实际的类型参数,而且,typename是作为标准C++的组成部分加入到C++中的,因此旧的程序更有可能只用关键字class。

2.在模板定义内部指定类型

    除了定义数据成员或函数成员之外,类还可以定义类型成员。例如,标准库的容器类定义了不同的类型,如size_type,使我们能够以独立与机器的方式使用容器。如果要在函数模板内部使用这样的类型,必须告诉编译器我们正在使用的名字指的是一个类型。必须是显式地这样做,因为编译器(以及程序的读者)不能通过检查得知,由类型形参定义的名字何时是一个类型何时是一个值。例如,考虑下面的函数:

template <class Parm, class U>
Parm fcn (Parm* array, U value)
{
    param :: size_type *p;
}

    我们知道size_type必定是绑定到Param的那个类型的成员,但是我们不知道size_type是一个类型成员的名字还是一个数据成员的名字,默认情况下,编译器假定这样的名字指定数据成员,而不是类型。

    如果希望编译器将size_type当做类型,则必须显式告诉编译器这样做:

template <class Parm, class U>
Parm fcn (Parm* array, U value)
{
    typename Parm :: size_type *p;
}

通过在成员名前加上关键字typename作为前缀,可以告诉编译器将成员当做类型。通过编写typename Parm :: size_type,指出绑定到Parm的类型的size_type成员是类型的名字。当然,这一声明给用来实例化fcn的类型增加了一个职责:那些类型必须具有名为size_type的成员,而且该成员是一个类型。
    OK,于是乎,大家就知道应该怎么消除bug了,如下:

template <class ElemType>
bool Complete (TreeNode <ElemType>* t)
{
    ......
    ......
    for (typename deque <TreeNode <ElemType>*> :: iterator iter = q.begin (); iter + 1 < q.end (); iter ++)
    {
         if (!(*iter -> Right ()) && (*(iter + 1) -> Left () || *(iter + 1) -> Right ()))
            {
                return false;
            }
     }
}

即,在deque前面加上typename关键字,告诉编译器,这里的iterator是个类型!

    再次编译,又出现了让人费解的错误:

D:\programming practice\acm practice\6-36\treelib.h|88|error: request for member 'Right' in......which is of non-class type 'TreeNode<char>*'|

D:\programming practice\acm practice\6-36\treelib.h|88|error: request for member 'Left' in ...... which is of non-class type 'TreeNode<char>*'|

D:\programming practice\acm practice\6-36\treelib.h|88|error: request for member 'Right' in ...... which is of non-class type 'TreeNode<char>*'|

    首先,对于gcc这种严重坑爹的错误提示,我表示愤慨和无奈。怎么解决?我摸索了很久才得到答案,代码如下:

for (typename deque <TreeNode <ElemType>*> :: iterator iter = q.begin (); iter + 1 < q.end (); iter ++)
        {
            if (!((*iter) -> Right ()) && ((*(iter + 1)) -> Left () || (*(iter + 1)) -> Right ()))
            {
                return false;
            }
        }

    看出来了吗??其实仅仅是在*iter和后面的两个*(iter + 1)外面加了个括号啊!坑爹啊!gcc要求真严格啊!

 


 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值