函数模板定义中的名字解析问题

eatta 2007-12-08 07:21:20
关于模板定义中的名字解析,C++primer介绍是分两步.跟模板参数类型无关的名字在模板定义时解析.而跟模板参数类型有关的名字在实例化时解析.
可是模板定义时是并没有定义的啊,这本书前边也说了,模板定义不是真正的函数定义,只是保留了内部表示.当实例化时才生成一个函数定义.
而实际中也确实是如此的.在模板定义时可以不顾语法乱写一通,只要不实例化,编译都能通过.
但如此一来,书上说的名字解析不就有问题了么?什么时候才叫模板定义呢?模板定义时似乎根本不解析任何东西啊.
是不是模板定义跟实例化是同时的?请大家解释下这个名字解析是怎么理解.
...全文
283 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
eatta 2007-12-16
  • 打赏
  • 举报
回复
楼上的懂就说下吧
靠自己固然是要,别人的经验也要听取,才能学得快
xiaoQ008 2007-12-15
  • 打赏
  • 举报
回复
我以前也不是很懂
看多了就慢慢懂了
凡是还得靠自己啊
sinux_1983 2007-12-15
  • 打赏
  • 举报
回复
不好意思,又搞错了。
我再查查!!!
eatta 2007-12-13
  • 打赏
  • 举报
回复
一楼的兄台就举的是类模板的例子,呵呵,你该不会又一次被误导了吧?
c++上明确表示函数模板定义时只是保留了内部表示的.
后来又说到定义时的解析问题,才让我觉得好像矛盾了.
eatta 2007-12-13
  • 打赏
  • 举报
回复
呵呵,类模板是不同.但是你还是没有看清楚我的问题.我说的是函数,你试试看函数模板吧
我也试过了,确实就乱写一些字母都能通过编译.你不妨再试试看.
我也用的vs2005.
没验证过的我是不会乱说出来的.
sinux_1983 2007-12-13
  • 打赏
  • 举报
回复
考完了,总算可以轻松一阵了。
查了一下书,C++程序设计语言(TC++PL)上是这么说的:
在模板定义时,要检查这个定义的语法错误,也可能包括另外一些能独立于特定模板参数集而检查出来的错误。
...编译器可以在定义点捕捉一些简单的语义错误,或者是以后再使用点再去做。
...与模板参数的使用有关的错误在模板使用之前不可能检查出来。
下面是测试程序(这儿用的是VS2005,我在dev-C++上面也试了,结果一样。):

#include <iostream>
using namespace std;

template<class T> class test_T
{
private:
T t //注意:这儿少了一个分号。有分号的话可以顺利通过编译
public:
test_T(T tt):t(tt)
{}


void printT()
{
cout<<t;
}
};

int main(void)
{
return 0;
}


下面是编译出错信息:
c:\documents and settings\yhsh\桌面\linshi\linshi\test.cpp(9) : error C2143: 语法错误 : 缺少“;”(在“public”的前面)
c:\documents and settings\yhsh\桌面\linshi\linshi\test.cpp(18): 参见对正在编译的类 模板 实例化“test_T<T>”的引用
生成日志保存在“file://c:\Documents and Settings\yhsh\桌面\linshi\linshi\Debug\BuildLog.htm”
linshi - 1 个错误,0 个警告
========== 生成: 0 已成功, 1 已失败, 0 最新, 0 已跳过 ==========
可以看出虽然,程序中没有用到这个模板,但是编译器仍然对它进行了语法检查。

我们再看一下下面的程序

#include <iostream>
using namespace std;

class temp
{};

template<class T> class test_T
{
private:
T t;
public:
test_T(T tt):t(tt)
{}


void printT()
{
cout<<t;
}
};

int main(void)
{
test_T<int> t1(5);
test_T<float> t2(5.5);

//test_T<temp> t3; //加上这两行的话编译器就会报错
//t3.printT();

t1.printT();
cout<<endl;
t2.printT();
return 0;
}



上面的代码能够顺利通过,但是如果加上注释的那两行代码的话,编译器就会报错。
错误信息太长,这儿就不贴了。这说明与类型相关的错误确实是在实例化点进行的。
应该说书上说的应该没问题。
至于:“也可能包括另外一些能独立于特定模板参数集而检查出来的错误。”
这句话,还没有想出验证的例子,期待高手,当然那就依赖与编译器了。
eatta 2007-12-10
  • 打赏
  • 举报
回复
谢谢楼上的了.
你如果有下载的地址的话发一个上来吧.呵呵.
还是期待有人来回答.毕竟去翻一本电子书找一个答案很困难的哦.如果是纸版的还容易找些
taodm 2007-12-10
  • 打赏
  • 举报
回复
你就先跳过吧。等你后来看《C++ Templates》时会看到答案的。
这个问题搞清楚了也对实际编程没有太多意义,不要花太多时间在上面。
sinux_1983 2007-12-10
  • 打赏
  • 举报
回复
不好意思,确实理解错了题意。明天有考试,正备小抄呢。后天再好好试试。哈哈。
我有《C++ Templates》的电子版(不过是繁体的),不知楼主是否有兴趣。
eatta 2007-12-10
  • 打赏
  • 举报
回复
呵呵,我没有搞混.
实例化跟解析的概念我也很清楚.我想你是没有把我要表达的意思弄清楚.跟1楼的兄台一样.
实际测试时,对模板的定义是不检查的,上边我已说过了.验证的最好方法就是乱写一通代码,结果编译照样通过,只要你不调用这个模板.
而书上也有明确说明,模板定义并不是函数定义,只在实例化时生成函数定义.模板定义时编译器保留了内部表示(这就解释了为什么乱写也能通过编译,因为根本不检查).
12楼的taodm应该知道答案.我又没那本书在手.只能期待高人解答了.
btw:数学中如果要说函数中的函数的话,其实是没有这种定义的.不过我想你要表达的应该是复合函数才对吧.它不是函数的函数,它只不过是自变量也是函数的函数而已.
sinux_1983 2007-12-10
  • 打赏
  • 举报
回复
我猜楼主可能是把实例化和解析搞混了。
实例化是模板生成函数的过程,而解析(在这儿)是指编译器对代码的检查。
你可以把模板想象成是函数的函数(好像叫二阶函数来着、、我数学太差),就是说类型作为一个参数来参与的。
就是说从定义模板到生成代码有两次检查,第一次检查时模板不知道参数,这时他只能检查与类型无关的语法(没办法),第二次也就是传进参数以后,模板根据类型,进行第二次语法检查,因为掌握了更多的信息。之后就实例化了。

我是菜鸟,纸上谈兵,仅供参考。
eatta 2007-12-10
  • 打赏
  • 举报
回复
我只是想弄清楚书上所讲的关于这方面的内容.
楼上的请告诉我吧.谢谢了
eatta 2007-12-09
  • 打赏
  • 举报
回复
通过测试我总结如下:
函数模板中的名字都是在实例化时解析的.不管是否与模板参数有关.如果函数模板是在一个非全局的名字空间的话.则函数体里的名字解析只考虑同一名字空间的名字.如果没有,再考虑外部空间的名字.但是,如果同一空间有该名字,则不再去看外部空间的名字.这会影响到重载时的候选函数.
我是在VS2005下测试的.不知是否与编译器有关.
如此一来的话,模板的设计者要么在模板函数里边都使用自己的组件,要么就全让用户来提供这些组件.因为设计者一旦提供了.则用户就再不能定义一个自己的同样名字同类型的组件了.
这是否不太合理啊?但实际测试中确实如此.谁来解释下啊
ryfdizuo 2007-12-09
  • 打赏
  • 举报
回复
真的?这到没有试过
^_^^_^
eatta 2007-12-09
  • 打赏
  • 举报
回复
不是这样子的.普通的函数你即使不调用它,编译时也会解析的.你可以试下在普通函数里乱写一通,不顾语法,编译时马上就出错了.即使没有调用.
但函数模板不同,你乱写也能通过编译,只要不实例化
ryfdizuo 2007-12-09
  • 打赏
  • 举报
回复
是模板定义时根本不解析任何东西嘛,只是保留一个表示而已.
我就觉得很奇怪.
-----------------------------
但是普通函数才是这样子的啊,
调用的时候才检查函数定义,只要申明正确,就能通过编译,
eatta 2007-12-09
  • 打赏
  • 举报
回复
对啊.模板的参数演绎是用的第二种名字解析.也就是在实例化时解析.
可是,在模板内与模板参数无关的,比如一个函数的调用,书上说是在定义时解析.可是模板定义时根本不解析任何东西嘛,只是保留一个表示而已.
我就觉得很奇怪.
ryfdizuo 2007-12-09
  • 打赏
  • 举报
回复
不好意思啊
我没有看过那本书,
不过模板实参演绎,好像是第二种名字解析,常看到的也就是模板实参演绎,实例化啊
??
eatta 2007-12-09
  • 打赏
  • 举报
回复
对啊.但是为什么实际上测试时表现的行为却好像不是那么一回事啊?
真的是编译器的问题?
但还有一个问题,书上也说过模板定义时只是保留内部表示的啊.可是到了说名字解析时又说了与模板参数无关的名字在定义时解析,这好像自相矛盾吧.
难道内部表示的同时还解析了那些与模板参数无关的名字?
ranrer 2007-12-09
  • 打赏
  • 举报
回复
Thinking in C++ 上也说分两步,还给了几个例子,可能和编译器有关:


//: C05:Lookup.cpp
// Only produces correct behavior with EDG,
// and Metrowerks using a special option.
#include <iostream>
using std::cout;
using std::endl;

void f(double) { cout << "f(double)" << endl; }

template<class T> class X {
public:
void g() { f(1); }
};

void f(int) { cout << "f(int)" << endl; }

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

The only compiler we have that produces correct behavior without modification is the Edison Design Group front end,[66] although some compilers, such as Metrowerks, have an option to enable the correct lookup behavior. The output should be:

f(double)

because f is a non-dependent name that can be resolved early by looking in the context where the template is defined, when only f(double) is in scope. Unfortunately, there is a lot of existing code in the industry that depends on the non-standard behavior of binding the call to f(1) inside g( ) to the latter f(int), so compiler writers have been reluctant to make the change.

Here is a more detailed example:[67]

//: C05:Lookup2.cpp {-bor}{-g++}{-dmc}
// Microsoft: use option –Za (ANSI mode)
#include <algorithm>
#include <iostream>
#include <typeinfo>
using std::cout;
using std::endl;

void g() { cout << "global g()” << endl; }

template<class T> class Y {
public:
void g() {
cout << "Y<" << typeid(T).name() << ">::g()” << endl;
}
void h() {
cout << "Y<" << typeid(T).name() << ">::h()” << endl;
}
typedef int E;
};

typedef double E;

template<class T> void swap(T& t1, T& t2) {
cout << "global swap” << endl;
T temp = t1;
t1 = t2;
t2 = temp;
}

template<class T> class X : public Y<T> {
public:
E f() {
g();
this->h();
T t1 = T(), t2 = T(1);
cout << t1 << endl;
swap(t1, t2);
std::swap(t1, t2);
cout << typeid(E).name() << endl;
return E(t2);
}
};

int main() {
X<int> x;
cout << x.f() << endl;
} ///:~

The output from this program should be:

global g()
Y<int>::h()
0
global swap
double
1

Looking at the declarations inside of X::f( ):

· E, the return type of X::f( ), is not a dependent name, so it is looked up when the template is parsed, and the typedef naming E as a double is found. This may seem strange, since with non-template classes the declaration of E in the base class would be found first, but those are the rules. (The base class, Y, is a dependent base class, so it can’t be searched at template definition time).

· The call to g( ) is also non-dependent, since there is no mention of T. If g had parameters that were of class type of defined in another namespace, ADL would take over, since there is no g with parameters in scope. As it is, this call matches the global declaration of g( ).

· The call this->h( ) is a qualified name, and the object that qualifies it (this) refers to the current object, which is of type X, which in turn depends on the name Y<T> by inheritance. There is no function h( ) inside of X, so the lookup will search the scope of X’s base class, Y<T>. Since this is a dependent name, it is looked up at instantiation time, when Y<T> are reliably known (including any potential specializations that might have been written after the definition of X), so it calls Y<int>::h( ).

· The declarations of t1 and t2 are dependent.

· The call to operator<<(cout, t1) is dependent, since t1 is of type T. This is looked up later when T is int, and the inserter for int is found in std.

· The unqualified call to swap( ) is dependent because its arguments are of type T. This ultimately causes a global swap(int&, int&) to be instantiated.

· The qualified call to std::swap( ) is not dependent, because std is a fixed namespace. The compiler knows to look in std for the proper declaration. (The qualifier on the left of the “::” must mention a template parameter for a qualified name to be considered dependent.) The std::swap( ) function template later generates std::swap(int&, int&), at instantiation time. No more dependent names remain in X<T>::f( ).

To clarify and summarize: name lookup is done at the point of instantiation if the name is dependent, except that for unqualified dependent names the normal name lookup is also attempted early, at the point of definition. All non-dependent names in templates are looked up early, at the time the template definition is parsed. (If necessary, another lookup occurs at instantiation time, when the type of the actual argument is known.)

If you have studied this example to the point that you understand it, prepare yourself for yet another surprise in the following section on friend declarations.
加载更多回复(3)

64,662

社区成员

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

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