说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

昨天那个namespace的帖子之后,把自己的疑惑解决了一下,希望能学到更多.

编辑:说三道四文库 发布时间:2018-05-23 11:29
HTML文档下载 WORD文档下载 PDF文档下载
/C++

首先,必须说一下C++标准库里,是如何使用namespace的。

要明白几点:

1,全局作用域其实也是一个namespace,只是编译器隐藏了它的展开。
2,C++标准库(STL)里充斥着泛型与重载。
3,模板类的成员函数,模板函数的实现都是直接写在头文件里,而不是分离编译.
4,既然全局作用域也是一个namespace,不同的源文件的全局作用域就对应于不同文件的同一个namespace一样。
5,全局作用域如何声明,如何防止重定义,和自定义namespace是遵循一样规则的。所以,非模板函数,
非模板类的成员函数定义与声明要分开,头文件里写声明,源文件里写定义。 这和模板的做法是不同的,要注意。

暂时说这些,其他的比较麻烦,拿源码来看,往下继续.


/C++
// string standard header
#pragma once
#ifndef _STRING_
#define _STRING_
#ifndef RC_INVOKED
#include <istream>

 #pragma pack(push,_CRT_PACKING)
 #pragma warning(push,3)

 #pragma warning(disable: 4189)
 #pragma warning(disable: 4172)

_STD_BEGIN //namespace std{

模板函数的各种定义!!

_STD_END //}

 #pragma warning(pop)
 #pragma pack(pop)

#endif /* RC_INVOKED */
#endif /* _STRING */


看到_STD_BEGIN和_STD_END,我已经做了宏替换的注释. 

这是<string>头文件里的形式,可以看到,标准库里所有的头文件都是包含在namespace std里的,
而namespace是可以叠加的,举个很易懂的例子。

#include <iostream>
#include <string>

int main()
{
std::ve       
//我没有包含<vector>,可以根据上边string的头文件的样子知道,
//此时我只能见到string头文件里那段namespace std,所以当我尝试std::vector的时候,我并不能找到vector。
//除非你#include <vector> ,那么当前源文件里就累加了两段namespace std,所以此时std::vector就能看到了。
STD::Interface('a');

    return 0;
}

根据这个小例子可以差不多理解namespace与头文件的结合,并且是如何给用户使用的。




/C++
昨天的帖子里的问题已经难寻踪迹,先贴上我下去之后设计的测试代码,
之后讲一下为什么看起来不可以的做法却能成功,
借此进一步理解namespace的作用原理(一定要记住全局作用域也是一个namespace,
假设叫namespace globle)。



interface.h
/C++


#ifndef INTERFACE_H
#define INTERFACE_H

#include "func.h" //##

namespace STD //标准库命名空间,相当于std
{
template <class T>
void Interface(T n)
{
func(n); //调用依赖于函数声明,所以引入了func.h,见##位置
}
}

#endif 



func.h

/C++#ifndef FUNC_H
#define FUNC_H

#include <iostream>

namespace STD
{
template <class T>
void func(T)
{
std::cout<<"T"<<endl;
}
}

#endif


main.cpp

/C++#include <iostream>
#include <string>
using namespace std;

#include "interface.h"

namespace STD
{
template <>
void func<char>(char)
{
cout<<"char"<<endl;
}
}

int main()
{
STD::Interface('a');

    return 0;
}


STD即模仿我们的C++标准库std,interface.h是STL提供的一个模板函数,它调用了func.h里的一个函数,
所以interface.h里需要包含func.h头文件,以便满足声明依赖要求(要调用一个函数,
只需要见到函数的声明@@@@@注,这里是模板函数,所以声明与定义不做区分,直接包含头文件即可)。

在main里,我们包含了interface.h,所以我们能够访问STD::Interface('a');

但我们必须注意,Interface.h里的Interface函数之前累加了一个从func.h里引入的函数func,
所以在main里我们同样可以访问到STD::func。 

/C++
那个帖子里,几位高人基本上使用的都是在main文件里扩充std来实现了一个函数的重载,
从而改变了STL的运作过程而不必修改STL库。 

从普通角度看,我们在main里特化了func,并且也把它加入到STD里,但是Interface的名字查找是往上进行的,
按道理是不会找到我们的特化的。 (昨天的帖子里不是特化,是重载,更能说明问题。) 
但却能运行起来,并且成功调用特化的版本,这正是因为interface之前有一个func模板函数的原因,
所以interface在查找func函数的范围扩增到了整个名字空间。

下面就是一个重载的例子,前两个namespace是标准库原有的,而我最新添加一个重载,一样能够得以调用,这就充分说明了
重载与特化对名字查找范围的影响,这个在全局作用域这个namespace里也是一样的,也就是说,namespace只是划分了空间,避免名字冲突,其实它没有做太多事。

#include <iostream>
#include <string>
using namespace std;

namespace STD
{
void b(int)
{
}
}

namespace STD
{
template <class T>
void a(T n)
{
b(n); //以上两个STD内的内容,理应调用b(1)
}
}

namespace STD //在main里临时添加重载,虽然函数位于a()之后
{
void b(double)
{
cout<<"double"<<endl;
}
}

int main()
{
STD::a(1.4);

    return 0;
}






代码不好用啊,算了.

谁看过昨天的帖子,有疑问的话看一下例子就懂了.
昨天的贴子同样收获匪浅,却没有lz这么高的认识
没啥疑问,疑问是csdn的ubb代码排版怎么又挂了……
问题是,C++标准不允许扩充std,只可以特化std里的部件。
总结起来就是:先往上找名字,找不到就出错,找到了但不匹配则在整个名字空间找,找到了且匹配则调用。
呃,楼主还是劳金手翻翻<C++ Primer>吧,貌似这个总结和我记忆中的偏差比较大。
额,说错了么。。。没在宿舍,求规则。
谁能没事背得出那么长的,又几乎一点用也没有的规则。
凡需要搞清此规则才能明白的代码,一般都可以定性为垃圾代码,直接打回重写。除非你是在实现boost这类库程序。
针对昨天那个帖子做了实验,感觉规则基本就是这样。如果真是我们来作一个库,需要重载或特化,那直接进去改就行标准库里根本不需要利用规则来安排名字查找,作好前置声明就行。  

这不就是因为有个前提条件,所以作了点编译器规则探索么。
这种东西,靠代码来探索基本都是浪费时间。
直接翻书、翻标准,更快更准。
引用 5 楼 qq120848369 的回复:
总结起来就是:先往上找名字,找不到就出错,找到了但不匹配则在整个名字空间找,找到了且匹配则调用。


额,不会去整个名字空间去找的。
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘