C语言的继任者?—— D语言

C 语言的继任者?—— D 语言
 
 
       现在看起来,大部分“新”的编程语言都跌入了两大“误区”:学术界关注的新语法变化和大公司关注的RAD(Rapid Application Development 快速应用程序开发)与Web开发,也许是时候产生一种实用型的新型语言了——正是在这种背景下,D语言诞生了。
 
在此,必须先提一下C语言。C语言是从BCPL语言和B语言演化而来的。1967年,Martin Richards开发出了BCPL(Basic Compound Programming Language)语言,其目的是为了编写操作系统和编译器。1970年,Ken Thompson在BCPL语言的基础上开发出了B语言,并在DEC PDP-7计算机上利用B语言实现了第一个Unix操作系统。
    C语言是贝尔实验室的Dennis Ritchie在B语言的基础上开发出来的,并被逐渐用于Unix操作系统的系统软件和应用软件的开发上。这就是说,C语言的前身是B语言,那么现在出现的D语言呢?它是什么一种类型的语言呢?那C语言会不会像B语言一样会被取代呢?接着往下看,就会明白了。
 
       最初,D语言由Walter Bright作为对C和C++语言的再设计而孕育于1999年12月,并在Walter Bright朋友与同事的建议下不断成长与发展。
       可在 http://www.digitalmars.com/d/dcompiler.html 处下载最新版本的Win32及x86 Linux编译器试用;并可在 http://home.earthlink.net/~dvdfrdmn/d处下载GCC的D语言前端编译器及 http://gdcmac.sourceforge.net/处下载GNU for Mac OS X。
 
       下面以Win32版本的D语言编译器来做简单说明:
相关文件
/dmd/bin/dmd.exe
D语言编译器
/dmd/bin/shell.exe
一个简单的命令行环境
/dmd/bin/sc.ini
编译器全局设置
/dmd/lib/phobos.lib
D语言运行时库
 
系统需求
32位Windows操作系统,如Windows XP
Dmd.zip for Win32(编译器)
Dmc.zip for Win32(链接器和工具)
 
安装
D语言中所有的工具都为命令行工具,所以必须在“命令提示符”窗口中运行。解压文件到一个特定的目录,dmd.zip将会创建名为/dmd的目录;dmc.zip将会创建名为/dm的目录。键入/dmd/bin/shell all.sh来运行。在目录/dmd/samples/d/下面,有一些简单的示例文件。
 
 
编译器参数与选项
dmd 文件名 -选项
 
文件名
扩展名
文件类型
D源代码
.d
D源代码
.di
D接口文件
.obj
目标文件
.lib
库文件
.exe
生成的可执行文件
.def
模块文件
.res
资源文件
 
-c
只编译,不链接
 
-cov
代码覆盖分析
 
-D
生成文档
 
-Dfdir
把文档写到dir目录中
 
-Dfnama
文档文件名为name
 
-d
允许“反对”功能
 
-debug
以debug模式编译
 
-debug=level
以小于等于level级别的debug模式编译
 
-debug=ident
以ident为识别符编译为debug模式
 
-g
加入符号调试信息
 
-H
生成D接口文件
 
-Hddir
把D接口文件写到dir目录中
 
-Hfname
生成的D接口文件名为name
 
--help
列出帮助信息
 
-inline
把函数展开为内联函数
 
-Ipath
引入(输入)库路径;通过分号可分隔不同的路径名,当有多个路径时,会以同一顺序进行搜索
 
-Lflag
把flag传递给链接器,如:/ma/li
 
-O
优化
 
-o-
不生成目标文件
 
-oddir
把目标文件写入到dir目录中
 
-ofname
把输出目录中的输出文件名设为name
 
-op
通常生成目标文件名时,将会去除 .d源代码文件的路径,而此选项将保留它
 
-profile
对生成代码的运行时性能作简单评价
 
-quiet
只显示重要的编译器信息
 
-release
编译为release版本
 
-unittest
编译为unittest代码,但仍打开断言
 
-v
显示详细信息
 
-version=level
编译为大于等于level级别的版本代码
 
-version=ident
以ident为识别符编译为版本代码
 
-w
打开警告信息
 
 
       链接器
       通常来说,在使用dmd编译器成功编译之后,就会直接调用链接器,如果不想链接,需在编译器加上-c选项。
       D语言程序必须链接到phobos.lib运行时库,在此库之前,还必须链接C语言运行时库snn.lib。只要库文件的目录在LIB环境变量路径名中,这会自动完成,通常会这样设置LIB:
Set LIB=/dmd/lib;/dm/lib
 
       如果dmd.exe在当前目录找不到链接器,就会到PATH路径中查找,如果想要使用一个特定的链接器,请设置LINKCMD环境变量,如:
set LINKCMD=/dm/bin/link
 
       另外,dmd会在同一目录中查找sc.ini初始化文件,如果此文件存在,那么文件中的环境变量设置将会取代现有的任何设置。环境变量跟在[Environment]之后,以name=value形式出现,注释是那些以分号打头的行,如:
 
; sc.ini file for dmd
; Names enclosed by %% are searched for in the existing environment
; and inserted. The special name %@P% is replaced with the path
; to this file.
[Environment]
LIB="%@P%/../lib";/dm/lib
DFLAGS="-I%@P%/../src/phobos"
LINKCMD="%@P%/../../dm/bin"
DDOCFILE=mysettings.ddoc
 
 
       D 接口文件
       当在D源代码文件中处理一个引入库的声明时,编译器会在D源代码文件对应的目录中查找此引入库,并从中提取源代码所需的信息。换句话说,编译器只会查找对应的D接口文件。接口文件中只含有引入一个模块所需的信息,而不是此模块的完整实现。
       此处不使用D源代码文件,而使用D接口文件的好处是:
Ø D接口文件通常来说体积更小,相比D源代码代码,可以被更快速地处理。
Ø 可用于隐藏源代码,例如,接口文件可与目标文件一同发售,而不用附带完整的源代码文件。
Ø D接口文件可通过对D源代码文件加上编译器选项-H生成,文件名后缀为 .di。此时当编译器解析一个输入库的声明时,它首先查找相应的 .di  D接口文件,再查找D源代码文件。
Ø D接口文件承担了一部分相当于C++头文件的任务,但不像C++头文件那样是必须的,同时也不是D语言的一部分,它只是编译器的一种特性,在构建过程中起到优化的作用。
 
 
       Bugs
       当前版本的D语言中还存在以下一些bug:
² 编译器有时会把行号弄错
² D运行时库phobos的功能还不是很足够
² 需要编写一个工具来把C语言头文件转换为D语言输入文件
² 数组赋值操作还未完成
² 成员函数的前置条件与后置条件都不能继承
² 不能在IDDE中运行
 
 
       D 语言 PK 其他语言
       下表是D语言与C、C++、C#、Java语言的大体功能对比。虽然很多语言功能都可以通过标准库得到加强,但下表只对比了语言本身的内置功能;此处只对比了官方标准化的功能,而没有对比诸如提议的功能、beta版本、或扩展功能。
 
  
 

功能
D
C
C++
C#
Java
垃圾回收
Yes
No
No
Yes
Yes
函数
 
 
 
 
 
函数代理
Yes
No
No
Yes
No
函数重载
Yes
No
Yes
Yes
Yes
函数返回参数
Yes
Yes
Yes
Yes
No
嵌套函数
Yes
No
No
No
No
数据函数
Yes
No
No
No
No
动态终止
Yes
No
No
No
No
安全类型参数
Yes
No
No
Yes
Yes
数组
 
 
 
 
 
轻量型数组
Yes
Yes
Yes
No
No
可变数组
Yes
No
No
No
No
位数据
Yes
No
No
No
No
内置字符串支持
Yes
No
No
Yes
Yes
数组限幅
Yes
No
No
No
No
数组边界检查
Yes
No
No
Yes
Yes
数组结合
Yes
No
No
No
No
强类型定义
Yes
No
No
No
No
字符串转换
Yes
No
No
Yes
No
别名
Yes
Yes
Yes
No
No
面向对象编程
 
 
 
 
 
面向对象
Yes
No
Yes
Yes
Yes
多重继承
No
No
Yes
No
No
接口
Yes
No
Yes
Yes
Yes
操作符重载
Yes
No
Yes
Yes
No
模块
Yes
No
Yes
Yes
Yes
动态类加载
No
No
No
Yes
Yes
嵌套类
Yes
Yes
Yes
Yes
Yes
内部适配器类
Yes
No
No
No
Yes
协变返回类型
Yes
No
Yes
No
Yes
属性
Yes
No
No
Yes
No
性能
 
 
 
 
 
内联汇编
Yes
Yes
Yes
No
No
对硬件的直接访问
Yes
Yes
Yes
No
No
轻量级对象
Yes
Yes
Yes
Yes
No
显式内存分配控制
Yes
Yes
Yes
No
No
不依赖于虚拟机
Yes
Yes
Yes
No
No
直接生成本地代码
Yes
Yes
Yes
No
No
泛型编程
 
 
 
 
 
模板类
Yes
No
Yes
Yes
Yes
模板函数
Yes
No
Yes
No
Yes
隐式模板函数实例化
No
No
Yes
No
No
部分及显式特化
Yes
No
Yes
No
No
模板值参数
Yes
No
Yes
No
No
模板之模板参数
Yes
No
Yes
No
No
Mixins(混合插入)
Yes
No
No
No
No
静态if
Yes
No
No
No
No
基于类型的表达式条件编译
Yes
No
No
No
No
typeof
Yes
No
No
Yes
No
for each
Yes
No
No
Yes
Yes
隐式类型推论
Yes
No
No
No
No
可靠性
 
 
 
 
 
略语编程
Yes
No
No
No
No
单元测试
Yes
No
No
No
No
静态构建指令
Yes
No
No
Yes
Yes
有保证的初始化
Yes
No
No
Yes
Yes
自动析构函数
Yes
No
Yes
Yes
No
异常处理
Yes
No
Yes
Yes
Yes
try-catch-finally
Yes
No
No
Yes
Yes
线程同步原语
Yes
No
No
Yes
Yes
兼容性
 
 
 
 
 
C风格语法
Yes
Yes
Yes
Yes
Yes
枚举类型
Yes
Yes
Yes
Yes
Yes
支持所有C语言类型
Yes
Yes
No
No
No
80位浮点数
Yes
Yes
Yes
No
No
复数与虚数
Yes
Yes
No
No
No
支持访问C语言
Yes
Yes
Yes
No
No
可用现有的调试器
Yes
Yes
Yes
No
No
结构成员数据排列控制
Yes
No
No
No
No
生成标准目标文件
Yes
Yes
Yes
No
No
预处理宏
No
Yes
Yes
No
No
其他
 
 
 
 
 
条件编译
Yes
Yes
Yes
Yes
No
源代码中支持Unicode
Yes
Yes
Yes
Yes
Yes
编制注释文档
Yes
No
No
Yes
Yes


 
 
C++ 程序员怎样转换到 D 语言
每一个有经验的C++程序员都积累了自己的一套编程风格与技巧,而这也是C++程序员的第二特征。有时,当学习一门新语言时,会不由自主地带入自己习惯的风格,这样就很难完全以新语言的特色来完成同样的任务。以下是一些常用的C++编程技巧,并演示如何在D语言中完成。
 
 
       定义构造函数
C++中的构造函数必须与类名相同
class Foo
{
       Foo(int x);
};
 
 
       在D语言中通过关键字this来定义
class Foo
{
       this(int x) { }
}
 
 
       基类初始化
       在C++中,可通过基类初始化语法来调用基类构造函数
class A { A() {... } };
class B : A
{
    B(int x)
       : A()              //调用基类构造函数
     {    ...
     }
};
 
 
       在D语言中,基类构造函数通过super语法调用
class A { this() { ... } }
class B : A
 
     this(int x)
     {    ...
       super();   //调用基类构造函数
       ...
     }
 
       此处C++的优越之处在于,基类的构造函数,可放在继承类构造函数的任何地方;但D语言也都通过一个构造函数调用另一个构造函数达到此目的。
 
class A
{      int a;
       int b;
       this() { a = 7; b = foo(); }
       this(int x)
       {
           this();
           a = x;
       }
}
 
       甚至在构造函数调用之前,成员也能被初始化为常量,故以上代码可改写为:
class A
{      int a = 7;
       int b;
       this() { b = foo(); }
       this(int x)
       {
           this();
           a = x;
       }
}
 
 
       结构比较
       在C++中,可以用一种简单、方便的方式来定义一个结构
struct A x, y;
...
x = y;
 
       以上不是对结构进行比较,如果要比较结构,请看以下代码:
#include <string.h>
 
struct A x, y;
 
inline bool operator==(const A& x, const A& y)
{
    return (memcmp(&x, &y, sizeof(struct A)) == 0);
}
...
if (x == y)
...
 
       注意,在此处必须对每一个需要比较的结构完成操作符重载,而在实现中不会有任何形式的类型检查,所以,C++不会去理会是否(x == y)此时发生了什么,而必须手工去实现特定的操作符重载,以验证此时到底发生了什么。
       在用memcmp()实现的 == 操作符重载中,潜伏了一个严重的bug,因为结构中不同数据的排列方式,会有一些“填充数据”存在,而C++是不允许这些“填充数据”被赋值;这样,即使两个结构中每个成员都有相同的值,也会因为这些“填充数据”的不同而导致最终比较的结果不一致。
       为了解决这个问题,可以进行成员比较,但不幸的是,这种方法也不太可靠,因为:1、如果结构中新添了一个成员,而在 == 操作符比较中忘了加入对这种成员的支持;2、浮点数的比较会存在差异,即使它们的位模式匹配。因此,在C++中,对此没有一个稳健的解决方案。
 
 
       在D语言中,以一种非常明白、直截了当的方式进行。
A x, y;
...
if (x == y)
    ...
 
 
 
       创建一个新的typedef 类型
       在C++中,typedef的功能非常弱,它不能真正引入一种新的类型,而在底层,编译器也不会把一个typedef类型与它所代表的类型区分开。
 
#define HANDLE_INIT ((Handle)(-1))
typedef void *Handle;
void foo(void *);
void bar(Handle);
 
Handle h = HANDLE_INIT;
foo(h);                   //没有捕捉到的bug
bar(h);                   //OK
 
       C++的方法是创建一个哑元结构,它的唯一目的就是进行类型检查,并对新类型时行重载。
 
#define HANDLE_INIT ((void *)(-1))
struct Handle
{    void *ptr;
 
    //默认构造函数
    Handle() { ptr = HANDLE_INIT; }
 
    Handle(int i) { ptr = (void *)i; }
 
    //转换为其所代表的类型
    operator void*() { return ptr; }
};
 
void bar(Handle);
 
Handle h;
bar(h);
h = func();
if (h != HANDLE_INIT)
    ...
 
 
       而在D语言中,没有必要为了合乎语言习惯而使用上述的写法,只需要编写:
 
typedef void *Handle = cast(void *)-1;
void bar(Handle);
 
Handle h;
bar(h);
h = func();
if (h != Handle.init)
    ...
       此处要注意,对typedef而言,是怎样提供一个默认构造函数的。
 
 
       友元
       在C++中,有时,两个有紧密关系但却不是继承来的类,需要访问各自的私有成员,此时就必须声明为友元:
 
class A
{
    private:
       int a;
 
    public:
       int foo(B *j);
       friend class B;
       friend int abc(A *);
};
 
class B
{
    private:
       int b;
 
    public:
       int bar(A *j);
       friend class A;
};
 
int A::foo(B *j) { return j->b; }
int B::bar(A *j) { return j->a; }
 
int abc(A *p) { return p->a; }
 
 
       在D语言中,友元的访问是通过同一模块中的某个成员来隐式实现的。D语言的做法是,有紧密关系的类,应该在同一module(模块)中。这是有道理的,通过隐式地允许对其他模块成员的友元访问,巧妙地解决了这个问题:
 
module X;
 
class A
{
    private:
       static int a;
 
    public:
       int foo(B j) { return j.b; }
}
 
class B
{
    private:
       static int b;
 
    public:
       int bar(A j) { return j.a; }
}
 
int abc(A p) { return p.a; }
 
       此时的私有属性可防止来自其他模块的、对成员的访问。
 
 
       操作符重载
       在C++中,通过一个结构来创建一个新的支持算术运算的数据类型,可非常方便地进行操作符重载,并把它与整数进行比较:
 
struct A
{
       int operator < (int i);
       int operator <= (int i);
       int operator > (int i);
       int operator >= (int i);
};
 
int operator < (int i, A &a) { return a >  i; }
int operator <= (int i, A &a) { return a >= i; }
int operator > (int i, A &a) { return a < i; }
int operator >= (int i, A &a) { return a <= i; }
       算上加、减、乘、除,共需要8个函数。
 
 
       D语言意识到比较操作符是一种基本的必须类型,所以只需要一个函数就行了:
 
struct A
{
       int opCmp(int i);
}
 
       编译器根据cmp函数,自动解析所有的<、<=、>、>=操作符,同样,也会处理好那些左操作数不是一个对象引用的情况。
       对其他类型的操作符重载,也是适用的,在D语言中使用操作符重载可不那么乏味,且利于减少错误,因为达到同样效果,D语言的代码量更少。
 
 
       命名空间
       在C++中,通常是在一个命名空间名称前加上using声明,以便在当前作用域中使用:
 
namespace Foo
{
    int x;
}
using Foo::x;
 
 
       而在D语言中,使用module取代了命名空间和#include,而且alias声明取代了using声明:
 
/** Module Foo.d **/
module Foo;
int x;
 
/**另一个module **/
import Foo;
alias Foo.x x;
 
       alias比单一目的的using声明更加灵活,alias也能被用于重命名符号(symbol)、引用模板成员、引用嵌套类类型,等等。
 
 
       RAII (Resource Acquisition Is Initialization) 资源的获取是通过初始化
       在C++中,类似内存之类的资源,全部需要显式地处理。因为在离开作用域时,析构函数会被自动调用,所以经常把释放资源的代码放在析构函数中:
 
class File
{    Handle *h;
 
    ~File()
    {
       h->release();
    }
};
 
 
       在D语言中,所有的资源释放问题都只不过是跟踪与释放内存,这在D语言中,是通过垃圾回收机制自动完成的。在资源中,使用率排在第二的是信号量与互斥锁,也是由D语言中的同步声明语句自动完成的。与C++相比,涉及到RAII的问题只有auto类,auto类在离开它们的作用域时,会自动运行析构函数。
 
auto class File
{    Handle h;
 
    ~this()
    {
       h.release();
    }
}
 
void test()
{
    if (...)
    {   auto File f = new File();
       ...
    } // 即使是通过异常退出作用域,也会调用f.~this()
}
 
 
 
       属性
       在C++中,一般惯例是定义一个代码域,其中带有面向对象编程的get与set函数:
 
class Abc
{
 public:
    void setProperty(int newproperty) { property = newproperty; }
    int getProperty() { return property; }
 
 private:
    int property;
};
 
Abc a;
a.setProperty(3);
int x = a.getProperty();
 
       所有的这些都非常简单,只是需要多打一点字而已,但在一个大型程序中,对getProperty()与setProperty()过多调用,会造成代码更加难以阅读。
 
 
       在D语言中,可使用普通的域语法来完成get与set属性,并且get和set会调用相应的方法。
 
class Abc
{
    // set
    void property(int newproperty) { myprop = newproperty; }
 
    // get
    int property() { return myprop; }
 
 private:
    int myprop;
}
 
Abc a;
a.property = 3;               //等于a.property(3)
int x = a.property; //等于int x = a.property()
 
       这样,D语言中的属性就能被当作一个简单的域名,另外,在一个继承类需要重载它们时,也省去了冗长的get与set属性定义,并且也容易实现接口类。
 
 
       递归模板
       对模板的一种高级应用是递归展开它们,并以特定的实现结束。一个计算价乘的模板如下:
 
template<int n> class factorial
{
    public:
       enum { result = n * factorial<n - 1>::result };
};
 
template<>class factorial<1>
{
    public:
       enum { result = 1 };
};
 
void test()
{
    printf("%d/n", factorial<4>::result); //输出24
}
 
 
       关于此示例的D语言版本是大致相同的,尽管有一点简单,但却是把单一模板成员提升到了括号中的命名空间:
 
template factorial(int n)
{
    enum { factorial = n * .factorial!(n-1) }
}
 
template factorial(int n : 1)
{
    enum { factorial = 1 }
}
 
void test()
{
    printf("%d/n", factorial!(4)); //输出24
}
 
 
 
       元模板
       此处引出的问题是:为一个单一整数类型创建一个typedef,在大小上至少都为n位。
 
       在C++中,无法基于模板参数的表达式结果进行条件编译,因此,所有的控制流程皆遵循模板参数的模式匹配,而不是多样的显式特定实现,更糟的是,也不能基于如“小于或等于”之类的关系进行模板的具体实现,所以在以下例子中使用了一种技巧,在其中模板可递归展开,并且每次都会递增模板值,直到满足某一特定条件。如果找不到匹配,会产生编译器堆栈递归溢出或内部错误,此时,最好的情况充其量也是一个奇怪的语法错误。此处需要一个预处理宏,以弥补缺少模板typedef。
 
#include <limits.h>
 
template< int nbits > struct Integer
{
    typedef Integer< nbits + 1 > :: int_type int_type ;
} ;
 
struct Integer< 8 >
{
    typedef signed char int_type ;
} ;
 
struct Integer< 16 >
{
    typedef short int_type ;
} ;
 
struct Integer< 32 >
{
    typedef int int_type ;
} ;
 
struct Integer< 64 >
{
    typedef long long int_type ;
} ;
 
//如果不支持所需的大小,那么“元”程序将会递增计数器,
//直到发生内部错误,或到达INT_MAX。
//而INT_MAX的具体实现并没有定义一个int_type,
//所以终会产生一个编译错误。
struct Integer< INT_MAX >
{
} ;
 
 
#define Integer( nbits ) Integer< nbits > :: int_type
 
#include <stdio.h>
 
int main()
{
    Integer( 8 ) i ;
    Integer( 16 ) j ;
    Integer( 29 ) k ;
    Integer( 64 ) l ;
    printf("%d %d %d %d/n", sizeof(i), sizeof(j), sizeof(k), sizeof(l));
    return 0 ;
}
 
       如果采用C++ Boost库,代码如下:
 
#include <boost/mpl/if.hpp>
#include <boost/mpl/assert.hpp>
 
template <int nbits> struct Integer
    : mpl::if_c<(nbits <= 8), signed char
    , mpl::if_c<(nbits <= 16), short
    , mpl::if_c<(nbits <= 32), long
    , long long>::type >::type >
{
    BOOST_MPL_ASSERT_RELATION(nbits, <=, 64);
}
 
#include <stdio.h>
 
int main()
{
    Integer< 8 > i ;
    Integer< 16 > j ;
    Integer< 29 > k ;
    Integer< 64 > l ;
    printf("%d %d %d %d/n", sizeof(i), sizeof(j), sizeof(k), sizeof(l));
    return 0 ;
}
 
 
       上述代码如果使用D语言编写,仍可使用递归模板,但有一个更好的方法,与C++不同的是,此方法非常容易看出具体的思路,并且可快速通过编译,如果未通过,还可给出一个切合实现的编译时错误信息。
 
template Integer(int nbits)
{
    static if (nbits <= 8)
       alias byte Integer;
    else static if (nbits <= 16)
       alias short Integer;
    else static if (nbits <= 32)
       alias int Integer;
    else static if (nbits <= 64)
       alias long Integer;
    else
       static assert(0);
}
 
int main()
{
    Integer!(8) i ;
    Integer!(16) j ;
    Integer!(29) k ;
    Integer!(64) l ;
    printf("%d %d %d %d/n",
       i.sizeof, j.sizeof, k.sizeof, l.sizeof);
    return 0;
}
 
 
       类型特性
       类型特性是在编译时找出类型属性的另一个术语。
 
       有一个用C++编写的如下例子,其判断模板参数类型是否为一个函数:
 
template<typename T> class IsFunctionT
{
    private:
       typedef char One;
       typedef struct { char a[2]; } Two;
       template<typename U> static One test(...);
       template<typename U> static Two test(U (*)[1]);
    public:
       enum { Yes = sizeof(IsFunctionT<T>::test<T>(0)) == 1 };
};
 
void test()
{
    typedef int (fp)(int);
 
    assert(IsFunctionT<fp>::Yes == 1);
}
 
       此模板依赖于SFINAE(Substitution Failure Is Not An Error 置换失败不是一个错误),但它的工作原理仍旧是一个高深的模板话题。
 
 
       在D语言中,无须借助于模板参数模式匹配也能实现SFINAE:
 
template IsFunctionT(T)
{
    static if ( is(T[]) )
       const int IsFunctionT = 1;
    else
       const int IsFunctionT = 0;
}
 
void test()
{
    typedef int fp(int);
 
    assert(IsFunctionT!(fp) == 1);
}
 
       上述的原理基于,如果一个类型是一个函数,那它根本不需要一个模板,也不需要试图创建一个非法的函数数组类型,并可通过表达式直接对它进行测试:
 
void test()
{
    typedef int fp(int);
 
    assert( is(fp == function) );
}
 
 
       看到这里,你是不是对D语言也有了一个大致的认识呢,这种新语言的魅力到底有多大呢,那还等什么,赶快动手啊,D语言的美好前景就等着你去探索呢。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值