关于内嵌依赖类型的问题

lgw26046044 2010-02-01 05:19:51
前两天作练习题的时候遇到了这样的一个问题:

//头文件screen.h
#ifndef SCREEN_H
#define SCREEN_H
#include<iostream>
#include<string>
template<int hi,int wid>
class Screen
{
public:
typedef std::string::size_type index;
Screen();
Screen(const std::string&);
//复制控制成员
Screen(const Screen&);
~Screen();
Screen operator=(const Screen&);
//其他成员函数
char get()const;
char get(index,index)const;
index get_cursor()const;
bool move(index,index)const;
bool set(char);
std::ostream& display(std::ostream&);
std::ostream& display(std::ostream&)const;
private:
std::string contents;
index cursor;
index height,width;
};

#endif

//定义文件screen.cpp

#include"Screen.h"
using namespace std;
template<int hi,int wid>
Screen<hi,wid>::Screen():
contents(hi*wid),cursor(0),height(hi),width(wid){}
template<int hi,int wid>
Screen<hi,wid>::Screen(const string&str):
contents(str),cursor(0),height(hi),width(wid){}
template<int hi,int wid>
Screen<hi,wid>::Screen(const Screen& sc):
contents(sc.contents),cursor(sc.cursor),height(sc.cursor),width(sc.width){}
template<int hi,int wid>
Screen<hi,wid>::~Screen()
{

}
template<int hi,int wid>
Screen<hi,wid> Screen<hi,wid>::operator=(const Screen&sc)
{
if(!(this->contents==sc.contents&&
this->cursor=sc.cursor&&
this->height=sc.height&&
this->width=sc.width))
{
this->contents=sc.contents;
this->cursor=sc.cursor;
this->height=sc.height;
this->width=sc.width;
}
return *this;
}
template<int hi,int wid>
char Screen<hi,wid>::get()const
{
return contents[cursor];
}
template<int hi,int wid>
char Screen<hi,wid>::get(index h,index w)const
{
if(h>height||w>width)
{
return '\0';
}
else
{
return contents[(h-1)*width+w-1];
}
}


//注意这个函数成员的定义:
template<int hi,int wid>
Screen<hi,wid>::index Screen<hi,wid>::get_cursor()const
{
return cursor;
}
template<int hi,int wid>
bool Screen<hi,wid>::move(index h,index w)const
{
if(h>height||w>width)
{
return false;
}
else
{
cursor=(h-1)*width+wid-1;
return true;
}
}
template<int hi,int wid>
bool Screen<hi,wid>::set(char c)
{
if(width==0||height==0)
return false;
else
{
contents[cursor]=c;
return true;
}
}
template<int hi, int wid>
ostream& Screen<hi,wid>::display(ostream&os)
{
for(int i=0;i<height*width;++i)
{
os<<contents[i];
if((i+1)%width==0)
{
os<<'\n';
}
}
return os;

}
template<int hi, int wid>
ostream& Screen<hi,wid>::display(ostream&os)const
{
for(int i=0;i<height*width;++i)
{
os<<contents[i];
if((i+1)%width==0)
{
os<<'\n';
}
}
return os;

}
编译器报错为:
error: expected constructor, destructor, or type conversion before "Screen"
||=== Build finished: 1 errors, 0 warnings ===|
我看了之后,想到了这个错误,我曾经好像见过,于是在
Screen<hi,wid>::index Screen<hi,wid>::get_cursor()const 这句之前加了一个关键字:typename
(这句就变成了:typename Screen<hi,wid>::index Screen<hi,wid>::get_cursor()const 这个样子)
然后再编译,就通过了。
我知道这个错误是关于,内嵌依赖类型的问题。
于是,我找出我曾经读过的C++ Primer和C++ 必知必会 这两本书,找到其中关于内嵌依赖类型的讲解,
但是这两本书中的讲解都是说:依赖于模板形参,嵌套于类中的类型使用时候要加关键字:typename
但是上面的那个类模板中的index却不是依赖于模板形参的呀,为什么还要加关键字typename呢?
还有就是书中所说的“嵌套于类中”这几个字中的“类”字指的是我定义的那个类,还是指的是作为模板形参的实参的那个类?
...全文
213 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
yyg990441 2010-02-18
  • 打赏
  • 举报
回复
template <int hi,int wid>
typename Screen <hi,wid>::index Screen <hi,wid>::get_cursor()const
{
return cursor;
}
以上是一个函数模板(类模板的所有"成员函数"其实都是函数模板)
楼主所谓的"内嵌依赖类型名"包含了三个名词:内嵌、依赖、类型名。
其中,
1.内嵌指定义在类名的定义中的。以上函数的返回值类型index就是定义在类模板Screen中的.
2.依赖是指依赖于一个模板参数,以上的返回值类型index是在类Screen<hi,wid>中定义的,所以
index依赖于类Screen<hi,wid>;而类Screen<hi,wid>是由类模板产生的,所以类Screen<hi,wid>
依赖于类模板Screen,hi和wid.又因为hi和wid是函数模板
template <int hi,int wid>
typename Screen <hi,wid>::index Screen <hi,wid>::get_cursor()const
{
return cursor;
}
的模板参数,所以说"类型index依赖于一个模板参数(其实是2个)"

3.类型名是指这里最终要指出的是个类型名,而不是变量。例如Screen <hi,wid>::index 完全有可能是类Screen <hi,wid>类里的一个static对象。而且当我们这样写的时候,C++默认就是解释为一个变量的。所以,为了和变量区分,必须使用typename告诉编译器。


lgw26046044 2010-02-18
  • 打赏
  • 举报
回复
帮自己顶一下,呵呵,等待牛人 光临
CSDMAdmimistrator 2010-02-10
  • 打赏
  • 举报
回复
编译出现问题的时候就加上就行了
用得着记住么
lgw26046044 2010-02-10
  • 打赏
  • 举报
回复
哦 那这个就是记住就可以了被
taodm 2010-02-09
  • 打赏
  • 举报
回复
C++很多时候就一个“死背规则”,没“理由”可言。
不要陷在自己给自己设的套中。
lgw26046044 2010-02-09
  • 打赏
  • 举报
回复
帮自己顶一下,
哈哈哈哈哈哈哈
lgw26046044 2010-02-04
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 nineforever 的回复:]
返回值不在Screen <hi,wid>的作用域内,所以编译器不知道index是它的内嵌类型

template <typename T>
struct A {
    typedef int type;
    type f();
};

template <typename T>
typename A <T>::type A <T>::f() //需要typename
{
    type a; //不需要typename
}
[/Quote]

八楼你说的似乎很有道理,但是能告诉我在那本书上有介绍么,你说的还是有点太简略了
lgw26046044 2010-02-04
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 taodm 的回复:]
Screen <hi,wid>::index Screen <hi,wid>::get_cursor()const
注意这里面的2个Screen <hi,wid>的地位、含义是不同的。
[/Quote]
7楼你能说的清楚一点么,有点太模糊了
macrojj 2010-02-03
  • 打赏
  • 举报
回复

typedef std::string::size_type index;
这是你自己定义的类型 。 用的时候 要用typename 表示 index是个类型 不然不认识





nineforever 2010-02-03
  • 打赏
  • 举报
回复
返回值不在Screen<hi,wid>的作用域内,所以编译器不知道index是它的内嵌类型

template<typename T>
struct A {
typedef int type;
type f();
};

template<typename T>
typename A<T>::type A<T>::f() //需要typename
{
type a; //不需要typename
}
taodm 2010-02-03
  • 打赏
  • 举报
回复
Screen <hi,wid>::index Screen <hi,wid>::get_cursor()const
注意这里面的2个Screen <hi,wid>的地位、含义是不同的。
lgw26046044 2010-02-02
  • 打赏
  • 举报
回复
4楼 你能做个简单的说明么?
lgw26046044 2010-02-02
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 pengzhixi 的回复:]
因为你要在模板里面使用string 的内嵌类型,所以就必须用typename.
[/Quote]

的确他是内嵌类型但并不依赖呀 ?
sjgh_1314 2010-02-01
  • 打赏
  • 举报
回复
Consider the following:

//: C05:TypenamedID.cpp {-bor}
// Uses 'typename' as a prefix for nested types.

template<class T> class X {
// Without typename, you should get an error:
typename T::id i;
public:
void f() { i.g(); }
};

class Y {
public:
class id {
public:
void g() {}
};
};

int main() {
X<Y> xy;
xy.f();
} ///:~

The template definition assumes that the class T that you hand it must have a nested identifier of some kind called id. Yet id could also be a static data member of T, in which case you can perform operations on id directly, but you can t create an object of the type id. In this example, the identifier id is being treated as if it were a nested type inside T. In the case of class Y, id is in fact a nested type, but (without the typename keyword) the compiler can t know that when it s compiling X.

If the compiler has the option of treating an identifier as a type or as something other than a type when it sees an identifier in a template, it will assume that the identifier refers to something other than a type. That is, it will assume that the identifier refers to an object (including variables of primitive types), an enumeration, or something similar. However, it will not cannot just assume that it is a type.

Because the default behavior of the compiler is to assume that a name that fits the above two points is not a type, you must use typename for nested names (except in constructor initializer lists, where it is neither needed nor allowed). In the above example, when the compiler sees typename T::id, it knows (because of the typename keyword) that id refers to a nested type and thus it can create an object of that type.

The short version of the rule is: if a type referred to inside template code is qualified by a template type parameter, you must use the typename keyword as a prefix, unless it appears in a base class specification or initializer list in the same scope (in which case you must not).

The above explains the use of the typename keyword in the program TempTemp4.cpp. Without it, the compiler would assume that the expression Seq<T>::iterator is not a type, but we were using it to define the return type of the begin( ) and end( ) member functions.

The following example, which defines a function template that can print any Standard C++ sequence, shows a similar use of typename:

//: C05:PrintSeq.cpp {-msc}{-mwcc}
// A print function for Standard C++ sequences.
#include <iostream>
#include <list>
#include <memory>
#include <vector>
using namespace std;

template<class T, template<class U, class = allocator<U> >
class Seq>
void printSeq(Seq<T>& seq) {
for(typename Seq<T>::iterator b = seq.begin();
b != seq.end();)
cout << *b++ << endl;
}

int main() {
// Process a vector
vector<int> v;
v.push_back(1);
v.push_back(2);
printSeq(v);
// Process a list
list<int> lst;
lst.push_back(3);
lst.push_back(4);
printSeq(lst);
} ///:~

Once again, without the typename keyword the compiler will interpret iterator as a static data member of Seq<T>, which is a syntax error, since a type is required.

Typedefing a typename
It s important not to assume that the typename keyword creates a new type name. It doesn t. Its purpose is to inform the compiler that the qualified identifier is to be interpreted as a type. A line that reads:

typename Seq<T>::iterator It;

causes a variable named It to be declared of type Seq<T>::iterator. If you mean to create a new type name, you should use typedef, as usual, as in:

typedef typename Seq<It>::iterator It;

Using typename instead of class
Another role of the typename keyword is to provide you the option of using typename instead of class in the template argument list of a template definition:

//: C05:UsingTypename.cpp
// Using 'typename' in the template argument list.

template<typename T> class X {};

int main() {
X<int> x;
} ///:~

To some, this produces clearer code.

Thinking in C++ Vol 2 - Practical Programming
lovesi3344 2010-02-01
  • 打赏
  • 举报
回复
坐等大牛
pengzhixi 2010-02-01
  • 打赏
  • 举报
回复
因为你要在模板里面使用string 的内嵌类型,所以就必须用typename.
lgw26046044 2010-02-01
  • 打赏
  • 举报
回复
顶一下 ,等待指点

65,210

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧